The basic core principles and core concepts of Spring learning source code


Core principles

AnnotationConfigApplicationContext

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

We rarely use Spring in the above way, but use Spring MVC, or Spring Boot, but they are essentially based on this method and need to create an ApplicationContext internally.

  • SpringBoot creates AnnotationConfigApplicationContext
  • SpringMVC creates XmlWebApplicationContext, which is based on xml similar to ClassPathXmlApplicationContext.

AnnotationConfigApplicationContext is the main class for research and study

IoC container loading process

The IoC container loading process can be divided into two steps

  • Prepare, register some underlying infrastructure tool classes for use in the container initialization process, then register the configuration class, and then refresh the container
  • Scan, parse various configured beans into BeanDefinition, and store them in BeanDefinitionMap
  • Traverse the BeanDefinitionMap, produce singletons, and cache them in the singleton pool singletonObjects

How to create an object in Spring

The process of new AnnotationConfigApplicationContext(Config.class) is the loading process of the IoC container, scanning to generate BeanDefinition, traversing to generate singleton Bean. Finally, manually call getBean(beanName) to get the corresponding Bean from the cache.

Bean creation process (life cycle)

class -> Reflection newInstance -> Original object -> Dependency injection (property assignment) -> A bunch of Aware -> Before initialization (@PostConstruct) -> Initialization (InitailizedBean) -> After initialization (AOP) -> Proxy object (Proxy object .target=original object) -> Bean

  • Instantiation, creating an object by calling a certain constructor of the class through reflection. If there are multiple constructors, a selection will be made, which is called an inferred constructor.
  • Dependency injection (property assignment), traverse fields in the object, automatic assignment with @Autowired
  • Aware weaving, determine whether the object is (that is, whether the class is implemented) BeanNameAware, ApplicationContextAware and other Aware, if so, force the call to the set method defined by the structure
  • @PostConstruct processing, determine whether there is a method with this annotation, and if so, execute the method
  • InitializingBean processing, determine whether the object is an instance of the interface, if so, execute the afterPropertiesSet method defined by the interface
  • Custom init-method method
  • Determine whether AOP is needed. If not, return the current object. If necessary, generate a proxy object and return it.
  • If it is a singleton bean, it is stored in the singleton pool.

Singleton prototype

The scope of Bean is divided into two types: singleton and prototype.

  • Singleton: Every time getBean gets the same object, it will be cached in the singleton pool after it is created for the first time.
  • Prototype: Every time getBean is created, it will not be placed in the singleton pool.

Lazy loaded beans will not be created and cached when the IoC container is loaded, but will be created and cached when used.

inferred constructor

When instantiating, the parameterless constructor is used by default (if parameters are written, there will be no parameterless constructor by default, unless explicitly defined)

If there is a parameterless constructor, use the parameterless constructor. If there is no parameterless constructor, determine how many parameterized constructors there are. If there is only one parameterized constructor, use this parameterized constructor. If there are multiple parameterless constructors, you cannot confirm which one to use, so Error: The default parameterless constructor cannot be found.

If there are multiple parameterized constructors, you can also add @Autowired to specify which parameterized constructor to use.

Where does the parameter object of the parameterized constructor come from?

  • First, search the container according to the type. If there is only one, use it directly. If there are multiple, filter based on the name. If the name matches, use it directly. If there is no match, an error will be reported.

dependency injection

First filter the container by type. If there is only one match, use it directly. If there are multiple matches, then match by name. If there is a match, use it directly. If there is no match, an error will be reported.

AOP dynamic proxy

In the last step of creating the bean, it will be judged whether AOP is needed, and if so, dynamic proxy will be used.

The general process for determining whether AOP is needed

  • Find all aspect beans
  • Traverse each method and determine whether annotations such as @Before and @After are written.
  • If written, determine whether the corresponding PointCut matches the currently created Bean.
  • If it matches, it means that the current Bean needs to do AOP.

The general process of doing AOP with CGLib

  • Generate a proxy class that inherits the original class. The proxy class holds the original class object target. Note that the original class object goes through the Bean creation process, including dependency injection, initialization and other processes.
  • The proxy class overrides the methods in the original class, and will eventually call the corresponding method of the target, but will add aspect logic before and after, roughly as follows
  • The last bean created returns a proxy object holding the original object.
public class UserService {
    
    
	@Autowired
	private OrderService orderService;
	public void test() {
    
    
		sout;
	}
}
class UserServiceProxy extend UserService {
    
    
	UserService target;
	public void test() {
    
    
		// @Before 的逻辑
		target.test();
		// @After 的逻辑
	}
}

The proxy class inherits from the original class, so the fields of the original class are also present in the proxy class. However, Spring does not do dependency injection for the proxy class because it is not necessary.

The proxy object is only used to enhance a certain method of the original object. If you need to use the dependency injection fields of the original object in the aspect logic, you can also get the JoinPoint.getTarget()original object to operate, and each field in the original object has already been injected.

affairs

After adding the @Transactional annotation to a method, a proxy class and proxy object will be generated for this class during the AOP stage.

The process of transaction proxy object execution method

  • Determine whether there is @Transactional annotation on the current method
  • If so, the transaction manager creates a database connection (doesn’t this connection come from the connection pool?)
  • Set the autoCommit of the connection to false (when there is no transaction annotation, the connection is obtained from the connection pool and is automatically submitted every time a SQL is executed)
  • Execution method, SQL in execution method
  • Call the connection's commit or rollback method

Reasons why transaction annotations are scheduled to expire

Whether the transaction will fail depends on which object executes the @Transactional annotation method.

  • Proxy object: will not expire
  • Original object: Invalid, because what is executed is a normal method, and there is no enhancement made by the proxy object.

The most common example is that there are two transaction methods a and b in the class, and b will be called in a. Executing the transactions of a and b separately will not be invalid, but when b is executed in a, the configuration on the transaction annotation of b will be Invalid

Because the process of executing a is like this, get the proxy object of the class, execute its a, first go through the aspect logic, create the connection, set not to submit automatically, and then execute target.a, which is the a method of the original object, the main body at this time It is the original object rather than the proxy object. When the b method is executed, the essence is this.b, the subject is still the original object, and there is no aspect logic, so the transaction annotation configuration of the b method in a will be invalid.

Of course, there are many other reasons that require detailed analysis.

Many other AOP failures have the same reason, all caused by self-calling.

There is a solution, which is self-reference. The class depends on injecting itself into self. At this time, self is the proxy object. When calling b in a, use self.b. In this way, the main body is the proxy object. There are aspects to strengthen the logic and the affairs of b. The configuration will take effect

Why does the @Configuration transaction take effect after adding it below?

@Configuration
public class Config {
    
    
	@Bean
	public TransactionManager transationManager() {
    
    
		return new DataSourceTransactionManager(dataSource());
	}
	@Bean
	public JdbcTemplate jdbcTemplate() {
    
    
		return new JdbcTemplate(dataSource())
	}
	@Bean
	public DataSource dataSource() {
    
    
		return new DataSource();
	}
}
  • Without adding time, calling dataSource() twice generates two different data sources. In the end, the transaction manager and template use different data sources.
  • In addition, there will be special processing. dataSource() will be considered as a Bean, and the same object is passed into both.

When obtaining a connection in JdbcTemplate, it will check whether the current is a transaction environment. If so, the thread-bound connection will be obtained TransactionSynchronizationManager.getResource(dataSource);from that is, the connection created by the transaction manager. You need to use the same data source object to get the same connection. In this way, the transaction manager's commit and rollback operations will take effect on the JdbcTemplate.

Core idea

There are many abstractions and tools in the Spring source code, which need to be understood in advance to make it easier when reading the source code.

BeanDefinition

BeanDefinition is used to record various information about Bean configuration.

  • class, indicating the Bean type
  • scope, indicating Bean scope, singleton or prototype, etc.
  • lazyInit: indicates whether the Bean is lazy loaded
  • initMethodName: Indicates the method to be executed when the Bean is initialized.
  • destroyMethodName: Indicates the method to be executed when the Bean is destroyed
  • There are a lot more…

There are two ways to define beans: declarative and programmatic. Beans defined in various ways will eventually be parsed into BeanDefinition and cached.

  • Declaration:
    • < bean/>
    • @Bean
    • @Component(@Service,@Controller)
  • Programmatic
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    
    // 生成一个BeanDefinition对象,并设置beanClass为User.class,并注册到ApplicationContext中
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
    beanDefinition.setBeanClass(User.class);
    context.registerBeanDefinition("user", beanDefinition);
    
    System.out.println(context.getBean("user"));
    

BeanDefinitionReader

Used to parse resources into BeanDefinition according to certain rules

AnnotatedBeanDefinitionReader

A class can be parsed into a BeanDefinition, including annotations on the class (@Conditional, @Scope, @Lazy, @Primary, @DependsOn, @Role, @Description)

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context);

// 将User.class解析为BeanDefinition
reader.register(User.class);

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

XmlBeanDefinitionReader

Can parse beans configured with <bean/> tags

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
int i = reader.loadBeanDefinitions("spring.xml");

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

ClassPathBeanDefinitionScanner

Scanner, but its function is similar to BeanDefinitionReader. It can scan, scan a certain package path, and parse the scanned classes.

If the @Component annotation exists on the scanned class, the class will be parsed into a BeanDefinition

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.scan("com.coder");

System.out.println(context.getBean("userService"));

BeanFactory

The root interface of the Spring container, the Bean factory, is responsible for creating and obtaining beans, and provides the definition of various getBean() methods.

DefaultListableBeanFactory

BeanFactory has a core implementation class DefaultListableBeanFactory , which can be used directly as BeanFactory and can be used instead of ApplicationContext, but it has fewer functions.

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

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

beanFactory.registerBeanDefinition("user", beanDefinition);

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

DefaultListableBeanFactory

The architecture of DefaultListableBeanFactory is as above, with many interfaces (capabilities) and classes

  • AliasRegistry : supports the alias function, defines the registration/acquisition/judgment/removal functions of alias, and supports the function of a BeanDefinition/Bean with multiple names.
  • SimpleAliasRegistry , maintained Map<String, String> aliasMap, here is the many-to-one relationship between alias and beanName, which makes it easy to find the original name from the alias (aliases can also be aliased), of course, you can also find all aliases from the original name
  • BeanDefinitionRegistry : defines the registration/acquisition/existence/removal functions of BeanDefinition
  • SingletonBeanRegistry : Defines functions such as registration/existence/getting/getting quantity/getting name of singleton beans
  • BeanFactory : Defines the functions of Bean acquisition/existence/getting alias/determining scope etc.
  • ListableBeanFactory : extends BeanFactory and provides methods for enumerating and retrieving bean instances in the container. You can easily obtain the names of all beans, obtain beans by type, and retrieve beans by conditions.
  • HierarchicalBeanFactory : extends BeanFactory and provides a hierarchical container structure and inheritance mechanism. Each sub-container can independently manage its own bean definition and instance. Child containers can inherit beans defined in the parent container, and can override or extend bean definitions in the parent container in the child container. Can achieve better modularization and organization, and flexibly manage and customize bean definitions and scopes
  • AutowireCapableBeanFactory : extends BeanFactory, provides the ability to automatically assemble beans, and can realize functions such as dependency injection, automatic assembly and decoupling.
  • ConfigurableBeanFactory , in addition to BeanFactory Bean, also provides tools for configuring BeanFactory. Added settings for setting parent BeanFactory, class loader (indicating that a class loader can be specified for loading classes), and setting Spring EL expression parser (indicating that The BeanFactory can parse EL expressions), set type conversion services (indicating that the BeanFactory can perform type conversion), add BeanPostProcessor (indicating that the BeanFactory supports Bean post-processors), merge BeanDefinitions, destroy a Bean, etc. Function
  • ConfigurableListableBeanFactory : In addition to ConfigurableBeanFactory, it also provides tools for analyzing and modifying bean definitions and pre-instantiating singletons
  • DefaultSingletonBeanRegistry : Mainly used to manage and maintain the registration and acquisition of singleton beans, singletonObjects are here
  • FactoryBeanRegistrySupport : Mainly used to support the registration and acquisition of FactoryBean, factoryBeanObjectCache is here
  • AbstractBeanFactory : The function is already very comprehensive, but it cannot automatically assemble and obtain beanNames. beanPostProcessors are here
  • AbstractAutowireCapableBeanFactory : has the function of automatic assembly
  • DefaultListableBeanFactory : It is a key component of the Spring container and is responsible for managing the registration, merging and search of BeanDefinitions, as well as the creation, assembly and destruction of Beans. It provides rich functionality and flexible configuration options and is one of the commonly used BeanFactory implementations in Spring applications. beanDefinitionMap is here

ApplicationContext

BeanFactory has a core sub-interface ApplicationContext , which is defined as follows

public interface ApplicationContext 
extends EnvironmentCapable, 
ListableBeanFactory, 
HierarchicalBeanFactory, 
MessageSource, 
ApplicationEventPublisher, 
ResourcePatternResolver

Insert image description here

  • HierarchicalBeanFactory: Has the function of obtaining the parent BeanFactory
  • ListableBeanFactory: Has the function of obtaining beanNames
  • ResourcePatternResolver: Resource loader, which can obtain multiple resources (file resources, etc.) at one time
  • EnvironmentCapable: can obtain the runtime environment (there is no function to set the runtime environment)
  • ApplicationEventPublisher: Has the function of broadcasting events (no function of adding event listeners)
  • MessageSource: has internationalization capabilities

ApplicationContext is positioned as the application context of Spring and is responsible for managing and organizing various parts of the application. From a code level, ApplicationContext is a BeanFactory. From an architectural level, ApplicationContext is a more advanced existence than BeanFactory. It governs BeanFactory. EnvironmentCapable, MessageSource and other components complete the corresponding functions, and BeanFactory is just a part of it.

According to this idea, GenericApplicationContext does not inherit DefaultListableBeanFactory but uses it as an attribute. All functions inherited from BeanFactory are entrusted to the DefaultListableBeanFactory held by it for execution, which is very reasonable.

The ApplicationContext interface inherits ListableBeanFactory and HierarchicalBeanFactory, but it is positioned as a high-level BeanFactory and only focuses on a certain degree of basic functions of BeanFactory. It does not require all the more powerful and detailed functions of the middle and lower layers.

ApplicationContext has two important implementation classes

  • AnnotationConfigApplicationContext
  • ClassPathXmlApplicationContext
AnnotationConfigApplicationContext

Insert image description here

  • Lifecycle : A common interface that defines start/stop life cycle control methods
  • ConfigurableApplicationContext : Added, adding event listeners, adding BeanFactoryPostProcessor, setting Environment, obtaining ConfigurableListableBeanFactory and other functions
  • AbstractApplicationContext : implements the universal context function, the famous refresh method is here
  • GenericApplicationContext : General application context concrete implementation class
  • AnnotationConfigRegistry : used to annotate and configure the application context. You can register a class as a BeanDefinition separately (can handle the @Configuration @Bean annotation on this class)
  • AnnotationConfigApplicationContext : is a convenient and powerful application context implementation, suitable for annotation-driven development. It realizes automated Bean registration and assembly by scanning and processing annotations, reducing tedious XML configuration
ClassPathXmlApplicationContext

Insert image description here
It also inherits AbstractApplicationContext, but compared to AnnotationConfigApplicationContext, its functions are not as powerful as AnnotationConfigApplicationContext. For example, BeanDefinition cannot be registered.

International MessageSource

Insert image description here

# messages.properties
test = 你好啊
# messages_en_US.properties
test = Hello
# messages_zh_CN.properties
test = 你好
@Configuration
public class MessageSourceConfig {
    
    

	@Bean
	public MessageSource messageSource() {
    
    
		ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
		resourceBundleMessageSource.setDefaultEncoding("UTF-8");
		resourceBundleMessageSource.setBasename("i18n.messages");
		return resourceBundleMessageSource;
	}

}
	public static void main(String[] args) {
    
    
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
		System.out.println(Locale.getDefault()); // zh_CN
		System.out.println(context.getMessage("test", null, Locale.getDefault())); // 你好
		System.out.println(context.getMessage("test", null, new Locale("zh_CN"))); // 你好
		System.out.println(context.getMessage("test", null, new Locale("en_US"))); // Hello
		System.out.println(context.getMessage("test", null, new Locale("de_DE"))); // 你好, 不存在, 走默认
	}
Resource loading getResource/getResources
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

Resource resource1 = context.getResource("file:/C:/coder/develop/workspace/idea/mrathena/spring/spring-analyze-test/src/main/java/com/coder/UserService.java");
System.out.println(resource1.contentLength());
System.out.println(resource1.getFilename());

Resource resource2 = context.getResource("https://www.baidu.com");
System.out.println(resource2.contentLength());
System.out.println(resource2.getURL());

// Resource resource3 = context.getResource("classpath:spring.xml");
// System.out.println(resource3.contentLength());
// System.out.println(resource3.getURL());

Resource[] resources = context.getResources("classpath:com/coder/**/*.class");
for (Resource resource : resources) {
    
    
	System.out.println(resource.contentLength());
	System.out.println(resource.getFilename());
}
Get the runtime environment
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

Map<String, Object> systemEnvironment = context.getEnvironment().getSystemEnvironment();
for (Map.Entry<String, Object> entry : systemEnvironment.entrySet()) {
    
    
	System.out.println(entry.getKey() + "\t\t" + entry.getValue());
}

System.out.println("=======");

Map<String, Object> systemProperties = context.getEnvironment().getSystemProperties();
for (Map.Entry<String, Object> entry : systemProperties.entrySet()) {
    
    
	System.out.println(entry.getKey() + "\t\t" + entry.getValue());
}

System.out.println("=======");

// 会包含所有的属性源, 包括上面的 SystemEnvironment 和 SystemProperties
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
for (PropertySource<?> propertySource : propertySources) {
    
    
	System.out.println(propertySource);
}

System.out.println("=======");

System.out.println(context.getEnvironment().getProperty("NO_PROXY"));
System.out.println(context.getEnvironment().getProperty("sun.jnu.encoding"));
System.out.println(context.getEnvironment().getProperty("Path"));

Note: You can use @PropertySource("classpath:spring.properties")the method to add parameters in a properties file to the runtime environment

event release
@ComponentScan
@Configuration
public class ApplicationListenerTest {
    
    

	@Bean
	public ApplicationListener<ApplicationEvent> applicationListener() {
    
    
		return event -> {
    
    
			System.out.println();
            System.out.println("接收到了一个事件\t\t" + event.getClass());
            System.out.println(event);
			if (event instanceof PayloadApplicationEvent) {
    
    
				System.out.println(((PayloadApplicationEvent) event).getPayload());
			}
        };
	}

	public static void main(String[] args) {
    
    
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationListenerTest.class);
		context.publishEvent("测试事件");
	}

}

type conversion

In the Spring source code, it may be necessary to convert String into other types, so the Spring source code provides some technologies to make object type conversion more convenient. Regarding the application scenarios of type conversion, you will encounter it when you read the source code later. a lot of.

PropertyEditor

Guess you like

Origin blog.csdn.net/mrathena/article/details/132474313