The overall context of Spring's underlying principles

How to create an object in Spring

AnnotationConfigApplicationContext and ClassPathXmlApplicationContext are both used to create Java objects. For example, calling getBean() will create an object.

In the Java language, an object must be created based on a certain class. Example code:

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

When context.getBean("userService") is called, an object will be created, but how does the getBean method know that "userService" corresponds to the UserService class?

Therefore, it can be analyzed that when the constructor of AnnotationConfigApplicationContext is called, which is the first line of code, some things will be done:

  1. Parse AppConfig.class and get the scan path
  2. Traverse all Java classes under the scan path. If annotations such as @Component and @Service are found on a certain class, Spring will record the class and store it in a Map, such as Map<String, Class>. (Actually, a similar Map does exist in the Spring source code, called BeanDefinitionMap, which will be discussed in subsequent courses)
  3. Spring will generate the beanName corresponding to the current class according to a certain rule, store it in the Map as the key, and use the current class as the value.

In this way, when context.getBean("userService") is called, the UserService class can be found based on "userService", so that the object can be created.

Bean creation process

So how does Spring create a Bean? This is the life cycle of Bean creation. The general process is as follows

  1. Use the constructor of the class to instantiate an object (but if there are multiple constructors in a class, Spring will choose one. This is called an inferred constructor)
  2. After getting an object, Spring will determine whether there are attributes annotated with @Autowired in the object, find these attributes and assign them by Spring (dependency injection)
  3. After dependency injection, Spring will determine whether the object implements the BeanNameAware interface, BeanClassLoaderAware interface, and BeanFactoryAware interface. If it does, it means that the current object must implement the setBeanName(), setBeanClassLoader(), and setBeanFactory() methods defined in the interface. Then Spring will call these methods and pass in the corresponding parameters (Aware callback)
  4. After the Aware callback, Spring will determine whether there is a method annotated with @PostConstruct in the object. If it exists, Spring will call this method of the current object (before initialization)
  5. Next, Spring will determine whether the object implements the InitializingBean interface. If it does, it means that the current object must implement the afterPropertiesSet() method in the interface, and Spring will call the afterPropertiesSet() method (initialization) in the current object.
  6. Finally, Spring will determine whether the current object requires AOP. If not, the Bean will be created. If AOP is required, Spring will perform dynamic proxying and generate a proxy object as the Bean (after initialization)

Through the last step, we can find that when Spring creates a Bean based on the UserService class:

  1. If AOP is not required, then the Bean is the object obtained by the constructor method of the UserService class.
  2. If AOP is required, then the Bean is the object instantiated by the UserService proxy class, not the object obtained by the UserService itself.

After the Bean object is created:

  1. If the current Bean is a singleton Bean, the Bean object will be stored in a Map<String, Object>. The key of the Map is beanName and the value is the Bean object. In this way, the corresponding Bean object can be obtained directly from the Map the next time you getBean. (Actually, in the Spring source code, this Map is a singleton pool)
  2. If the current Bean is a prototype Bean, there will be no other subsequent actions and a Map will not be stored. The above creation process will be executed again the next time getBean is used to obtain a new Bean object.

inferred constructor

When Spring generates beans based on a certain class, it needs to use the constructor of the class to instantiate an object. But if there are multiple constructors for a class, which one will Spring use?

Spring's judgment logic is as follows:

  1. If a class has only one constructor, Spring will use this constructor regardless of whether the constructor is a parameterless constructor or a parameterized constructor.
  2. If a class has multiple constructors
    a. Among these constructors, there is a parameterless constructor, then Spring will use this parameterless constructor
    b. Among these constructors, there is no parameterless constructor. Then Spring will report an error

Spring's design idea is this:

  1. If a class has only one constructor, then you have no choice but to use this constructor
  2. If there are multiple constructors in a class, Spring does not know how to choose, so it will look at whether there is a no-parameter constructor, because the no-parameter constructor itself represents a default meaning.
  3. However, if an @Autowired annotation is added to a certain constructor, it means that the programmer tells Spring to use this annotated method, and Spring will use this @Autowired annotated constructor.

What needs to be noted is that if Spring chooses a parameterized constructor, Spring needs to pass in parameters when calling this parameterized constructor. So where does this parameter come from?

Spring will find the Bean object in Spring based on the type and name of the input parameter (taking a singleton Bean as an example, Spring will find it from the Map of the singleton pool):

  1. First search based on the input parameter type. If only one is found, use it directly as the input parameter.
  2. If multiple types are found, the only one is determined based on the input parameter name.
  3. If it is not found in the end, an error will be reported and the current Bean object cannot be created.

Determine which construction method to use and determine the Bean object that takes the parameters. This process is called inferring the construction method.

AOP general process

AOP is dynamic proxy. In the process of creating a Bean, Spring will determine in the last step whether the Bean currently being created needs AOP, and if so, it will perform dynamic proxy.

How to determine whether the current Bean object needs AOP:

  1. Find all aspect beans
  2. Traverse each method in the aspect to see if annotations such as @Before and @After are written
  3. If written, determine whether the corresponding Pointcut matches the class of the current Bean object.
  4. If it matches, it means that the current Bean object has a matching Pointcut, indicating that AOP is required.

The general process of using cglib for AOP:

  1. Generate the proxy class UserServiceProxy, and the proxy class inherits UserService
  2. The proxy class overrides the methods of the parent class, such as the test() method in UserService.
  3. There will also be a target attribute in the proxy class, and the value of this attribute is the proxy object (that is, the object instantiated through the inferred construction method of the UserService class, and the object that has undergone dependency injection, initialization, etc. steps)
  4. The logic when the test() method in the proxy class is executed is as follows:
    a. Execute aspect logic (@Before)
    b. Call target.test()

When we get the Bean object of UserService from the Spring container, we get the object generated by UserServiceProxy, which is the proxy object.

UserService proxy object.test()—>Execute aspect logic—>target.test(). Note that the target object is not a proxy object, but a proxy object.

Spring transaction

When we add the @Transactional annotation to a method, it means that the method will start a Spring transaction when called, and the Bean object corresponding to the class where this method is located will be the proxy object of the class.

Steps when the Spring transaction proxy object executes a method:

  1. Determine whether the currently executed method has the @Transactional annotation
  2. If it exists, use the transaction manager (TransactionMananger) to create a new database connection
  3. Modify the autocommit of the database connection to false
  4. Execute target.test() to execute the business logic code written by the programmer, that is, execute sql
  5. If no exception occurs after execution, submit, otherwise roll back

Criteria for judging whether a Spring transaction will fail: When a method annotated with @Transactional is called, it must be judged whether it is directly called by the proxy object. If so, the transaction will take effect, if not, it will fail.

Guess you like

Origin blog.csdn.net/beautybug1126/article/details/132245357