Detailed explanation of the core concepts in Spring (see the necessary source code)

BeanDefinition

Definition of Bean. In Spring, how can we define a Bean?

  1. @Bean
  2. @Component(@Service,@Controller)

Also, you can use BeanDefinition

For example, we can define a Bean by defining a BeanDefinition object:

// A BeanDefinition is defined
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
// The type of the current Bean object
beanDefinition.setBeanClass(User.class);

// Register BeanDefinition in BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("user", beanDefinition);

// Get Bean
System.out.println(beanFactory.getBean("user"));

We can also set other properties of a Bean through BeanDefinition

beanDefinition.setScope("prototype"); // Set scope
beanDefinition.setInitMethodName("init"); // Set initialization method
beanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE); // Set up auto assembly model

wait

In short, beans defined by @ Bean, @ Component and other methods will eventually be resolved into BeanDefinition objects.

BeanDefinition can be understood as a concept at the underlying source level, or as a way of using an API provided by Spring.

BeanDefinitionReader

BeanDefinitionReader is divided into several categories:

AnnotatedBeanDefinitionReader

A class can be directly converted to BeanDefinition, and the annotations on the class will be parsed

Note: the annotations it can resolve are: @ Conditional, @ Scope, @ Lazy, @ Primary, @ DependsOn, @ Role, @ Description

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(beanFactory);

// Resolve User.class to BeanDefinition
annotatedBeanDefinitionReader.register(User.class);

System.out.println(beanFactory.getBean("user"));

XmlBeanDefinitionReader

Labels can be parsed

XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");

System.out.println(beanFactory.getBean("user"));

ClassPathBeanDefinitionScanner

This is not a BeanDefinitionReader, but its function is similar to that of BeanDefinitionReader. It can scan, scan a package path, and parse the scanned class. For example, if the @ Component annotation exists on the scanned class, the class will be parsed as a BeanDefinition

BeanFactory

In Spring, the core implementation class of BeanFactory is DefaultListableBeanFactory


It implements many interfaces, indicating that it has many functions:

  1. Alias registry: supports alias function. A name can correspond to multiple aliases
  2. BeanDefinitionRegistry: you can register, save, remove, and obtain a BeanDefinition
  3. BeanFactory: bean factory, which can obtain a bean object according to the name, type, or alias of a bean
  4. Singletonbean registry: you can directly register and obtain a singleton Bean
  5. Simpleliasregistry: it is a class that implements the functions defined in the alias registry interface and supports alias functions
  6. ListableBeanFactory: on the basis of BeanFactory, other functions are added to obtain the beanNames of all beandefinitions, the corresponding beanNames according to a type, and the mapping relationship of {type: corresponding Bean} according to a type
  7. Hierarchical BeanFactory: on the basis of BeanFactory, the function of obtaining parent BeanFactory is added
  8. DefaultSingletonBeanRegistry: it is a class that implements the SingletonBeanRegistry interface and has the function of directly registering and obtaining a singleton Bean
  9. ConfigurableBeanFactory: on the basis of hierarchical BeanFactory and SingletonBeanRegistry, it adds settings for parent BeanFactory, class loader (indicating that a class loader can be specified for class loading), Spring EL expression parser (indicating that the BeanFactory can parse EL expression), and type conversion service (indicates that the BeanFactory can be type converted), BeanPostProcessor can be added (indicates that the BeanFactory supports the post processor of a Bean), BeanDefinition can be merged, a Bean can be destroyed, and so on
  10. FactoryBean registrysupport: supports the functions of FactoryBean
  11. AutowireCapableBeanFactory: it directly inherits BeanFactory. Based on BeanFactory, it supports automatic assembly of beans during the process of creating beans
  12. AbstractBeanFactory: it implements the ConfigurableBeanFactory interface and inherits the FactoryBeanRegistrySupport. This BeanFactory has comprehensive functions, but it cannot automatically assemble and obtain beanNames
  13. ConfigurableListableBeanFactory: inherits ListableBeanFactory, AutowireCapableBeanFactory and ConfigurableBeanFactory
  14. AbstractAutowireCapableBeanFactory: inherits AbstractBeanFactory, implements AutowireCapableBeanFactory and has the function of automatic assembly
  15. DefaultListableBeanFactory: it inherits AbstractAutowireCapableBeanFactory and implements the ConfigurableListableBeanFactory interface and BeanDefinitionRegistry interface. Therefore, DefaultListableBeanFactory has powerful functions

Through the above analysis, we can know that we can do many things through DefaultListableBeanFactory, such as:

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// Register BeanDefinition
beanFactory.registerBeanDefinition("user", beanDefinition);
// Register alias
beanFactory.registerAlias("user", "user1");
// Register BeanPostProcessor
beanFactory.addBeanPostProcessor(new LubanBeanPostProcessor());

// Get Bean object
System.out.println(beanFactory.getBean("user1"));
// Get beanNames by type
System.out.println(beanFactory.getBeanNamesForType(User.class));

ApplicationContext

Firstly, ApplicationContext is an interface, which can be understood as a special BeanFactory

  1. Hierarchical BeanFactory: it has the function of obtaining the parent BeanFactory
  2. ListableBeanFactory: it has the function of obtaining beanNames
  3. Resourcepattern resolver: a resource loader that can obtain multiple resources (file resources, etc.) at one time
  4. EnvironmentCapable: you can get the runtime environment (the runtime environment function is not set)
  5. ApplicationEventPublisher: has the function of broadcasting events (without the function of adding event listeners)
  6. MessageSource: with internationalization function

There are two more important implementation classes:
7. AnnotationConfigApplicationContext
8. ClassPathXmlApplicationContext

AnnotationConfigApplicationContext

  1. ConfigurableApplicationContext: inherits the ApplicationContext interface and adds functions such as adding event listener, adding beanfactoryprocessor, setting Environment, and obtaining ConfigurableListableBeanFactory
  2. AbstractApplicationContext: implements the ConfigurableApplicationContext interface
  3. GenericApplicationContext: inherits AbstractApplicationContext, implements the BeanDefinitionRegistry interface, has all the functions of ApplicationContext, and can register BeanDefinition. Note that this class has an attribute (DefaultListableBeanFactory beanFactory)
  4. AnnotationConfigRegistry: you can register a class as BeanDefinition separately (you can handle the @ Configuration annotation on this class, and you can already handle the @ Bean annotation), and scan it at the same time
  5. AnnotationConfigApplicationContext: inherits GenericApplicationContext, implements the annotationconfigaregistry interface, and has all the above functions

ClassPathXmlApplicationContext


It also inherits AbstractApplicationContext, but compared with AnnotationConfigApplicationContext, its function is not as powerful as AnnotationConfigApplicationContext. For example, BeanDefinition cannot be registered

internationalization

First define a MessageSource:

@Bean
public MessageSource messageSource() {
	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
	messageSource.setBasename("messages");
	return messageSource;
}

With this Bean, you can use the MessageSource anywhere you want to internationalize.
At the same time, because ApplicationContext also has the function of nationalization, it can be used directly as follows:

annotationConfigApplicationContext.getMessage("test", null, new Locale("en_CN"))

Resource loading

ApplicationContext also has the function of resource loading. For example, you can directly use ApplicationContext to obtain the content of a file:

Resource resource = annotationConfigApplicationContext.getResource("file://D:\\IdeaProjects\\spring-framework\\luban\\src\\main\\java\\com\\luban\\entity\\User.java");
System.out.println(resource.contentLength());

You can think about it. If you do not use ApplicationContext, but implement this function yourself, it will take more time.

For example, you can:

Resource resource = annotationConfigApplicationContext.getResource("classpath:com/luban/entity/User.class");
System.out.println(resource.contentLength());

You can also get multiple at once:

Resource[] resources = annotationConfigApplicationContext.getResources("classpath:com/luban/service/*.class");
for (Resource resource : resources) {
	System.out.println(resource.contentLength());
}

This function uses the policy mode.

Get runtime environment

// Gets the environment of the operating system allowed by the JVM
annotationConfigApplicationContext.getEnvironment().getSystemEnvironment();

// Get some properties of the JVM itself, including those set by - D
annotationConfigApplicationContext.getEnvironment().getSystemProperties();

// You can also directly obtain the properties in an environment or properties file
annotationConfigApplicationContext.getEnvironment().getProperty("lubanyyy")

Note that you can use

@PropertySource("classpath:application.properties")

To add parameters in a properties file to the runtime environment

Event release

Define an event listener first

@Bean
public ApplicationListener applicationListener() {
	return new ApplicationListener() {
		@Override
		public void onApplicationEvent(ApplicationEvent event) {
			System.out.println("An event was received");
		}
	};
}

Then publish an event

annotationConfigApplicationContext.publishEvent("kkk");

Type conversion

PropertyEditor

Type conversion tool classes provided in JDK

public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {

	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		User user = new User();
		user.setName(text);
		this.setValue(user);
	}
}
StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
propertyEditor.setAsText("1");
User value = (User) propertyEditor.getValue();
System.out.println(value);

How to register a PropertyEditor with Spring:

@Bean
public CustomEditorConfigurer customEditorConfigurer() {
	CustomEditorConfigurer customEditorConfigurer = new CustomEditorConfigurer();
	Map<Class<?>, Class<? extends PropertyEditor>> propertyEditorMap = new HashMap<>();
	propertyEditorMap.put(User.class, StringToUserPropertyEditor.class);
	customEditorConfigurer.setCustomEditors(propertyEditorMap);
	return customEditorConfigurer;
}

Suppose there are the following beans:

@Component
public class UserService {

	@Value("true")
	User test;

	public void test() {
		System.out.println(test);
	}
}

Then the test attribute can normally complete the attribute assignment

ConversionService

The type transformation service provided in Spring is more powerful than the property editor

public class StringToUserConverter implements ConditionalGenericConverter {

	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return sourceType.getType().equals(String.class) && targetType.getType().equals(User.class);
	}

	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		return Collections.singleton(new ConvertiblePair(String.class, User.class));
	}

	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		User user = new User();
		user.setName((String)source);
		return user;
	}
}
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToUserConverter());
User value = conversionService.convert("1", User.class);
System.out.println(value);

How to register ConversionService with Spring:

@Bean
public ConversionServiceFactoryBean conversionService() {
	ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
	conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToUserConverter()));

	return conversionServiceFactoryBean;
}

TypeConverter

It integrates the functions of PropertyEditor and ConversionService and is used internally in Spring

SimpleTypeConverter typeConverter = new SimpleTypeConverter();
typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
//typeConverter.setConversionService(conversionService);
User value = typeConverter.convertIfNecessary("1", User.class);
System.out.println(value);

BeanPostProcessor

The post processor of a Bean can interfere in the process of creating each Bean. It is an attribute in BeanFactory, which is described in detail in the life cycle of a Bean.

BeanFactoryPostProcessor

The post processor of the Bean factory belongs to an attribute in the ApplicationContext. After the ApplicationContext instantiates a BeanFactory, it can continue to process the BeanFactory using the BeanFactory postprocessor.

Programmers can indirectly set BeanFactory through BeanFactory postprocessor. For example, the CustomEditorConfigurer above is a BeanFactory postprocessor, through which we can add a custom PropertyEditor to BeanFactory.

FactoryBean

Allows programmers to customize an object and indirectly put it into the Spring container through FactoryBean to become a Bean.

So what's the difference between @ Bean and @ Bean? Because @ Bean can also customize an object to make it a Bean.

The difference is that using FactoryBean can be more powerful, because you can implement other interfaces in Spring by defining an XxFactoryBean class. For example, if you implement the BeanFactoryAware interface, you can obtain the Bean factory in your XxFactoryBean, so as to use the Bean factory to do more you want to do, but @ Bean can't.

Posted by loopykd on Mon, 06 Dec 2021 21:07:24 -0800