[Spring Topic] Analysis of Spring's underlying core principles - guidance

foreword

Spring, it can be said that most of our Java players [the most familiar stranger]. Eight words to describe: half-understood, will
insert image description here
you say simple application, we all know it, if you really want to expand and say two sentences, then you can only come to these two sentences: this is the first sentence, followed by the second Sentence, well, I'm done.
insert image description here
But ah xdm,It is said that Spring is a very, very, very good source code. It not only has rich design pattern application scenarios, but also the code is beautifully written and organized, so I highly recommend everyone to learn it. In addition to being able to pretend in daily life, you can also enrich your knowledge and improve your ability to write code.

read navigation

Readers: People who have experience in Spring development

Pre-knowledge

Q1: Can you describe the JVM object creation process?

Answer: Look at the picture and talk:
insert image description here

  1. Class loading: Before using a class, the Java virtual machine needs to load the bytecode of the class into memory. Class loading is the core process of the Java virtual machine, which is responsible for finding the bytecode file of the class and loading it into the method area of ​​the memory. Class loading includes five phases: loading, verification, preparation, parsing, and initialization.
  2. Allocating memory: After the class loading is complete, the Java virtual machine allocates memory space for the object. Memory allocation is usually performed on the heap (Heap), but there are also objects in some special cases that can allocate memory on the stack (Stack), such as local objects on the thread stack.
  3. Instantiation (initialization of zero value): After allocating memory, the Java virtual machine initializes the memory space of the object to a zero value. This includes default values ​​for primitive types (such as 0, false, etc.) and default values ​​(null) for reference types.
  4. Set the object header: The layout of the Java object in memory includes two parts: the object header and the instance data. The object header stores some metadata, such as the object's hash code, lock status, etc. During object creation, the Java virtual machine sets the value of the object header.
  5. Executing the constructor: The last step in object creation is to execute the constructor. The constructor is used to initialize the instance data of the object and perform other necessary initialization operations. The constructor can be the default constructor of the class or a custom constructor.
  6. Return object reference: After the object is created, the Java virtual machine returns a reference to the object. Through the reference, the program can manipulate the properties and methods of the object.

PS: Why are you asking this question? Because Spring is an IOC technology, no matter how much it plays out, it must follow this basic process to create objects. However, I can tell you in advance that Spring IOC has added a lot of new slots in this process, and enriched the functions of IOC through hot swapping!

Q2: What are the characteristics of Spring?

Answer: The characteristics of Spring are the two concepts of IOC and AOP! It can even be said that:Spring is an IOC container that implements AOP technology. (container, container, container)

Q3: What is IOC and what is AOP?
Answer: The following answer comes from Baidu [Wen Xin Yi Yan]:

  • IOC (Inversion of Control) is a design pattern (thought) that allows the creation and management of objects to be handled by the Spring container instead of creating objects directly in the code . By using IOC, object dependencies can be decoupled from the code, making the code more flexible, maintainable and testable.
  • AOP (Aspect-Oriented Programming) is also a design pattern (thought), which uses pre-compilation and runtime dynamic proxy to dynamically add functions to programs without modifying the source code . AOP solves problems that cannot be solved in object-oriented programming, such as transaction management, security, logging, etc.

The Spring framework makes the program more modular, flexible and easy to maintain by implementing IOC and AOP. At the same time, Spring also provides many other modules and functions, such as DAO, ORM, WebMVC, etc., making it a powerful Java development framework.

Summary of pre-knowledge

From the above question, we mentioned a very important thing, namely:Spring is an IOC container that implements AOP technology. Moreover, it also generally describes the concepts of IOC and AOP. Now that we also know that IOC actually manages the creation of objects, then when it comes to object creation, it must be inseparable from the process of object creation we mentioned in Q1. Moreover, no matter how the object is created or who creates it, there is no way to leave the above process.
In fact, what I can tell you in advance is that the process of creating objects in IOC is nothing more than enriching some details and adding some extension points in the above object creation process to provide support for Spring function realization.

Course content

In order to carry out the research on the source code of Spring, we will briefly talk about some core knowledge points of Spring here, so that everyone can have a clear understanding of some basic logic at the bottom of Spring.

1. Starting the Spring container

I think that friends who have experienced the SSM/SSH era will be familiar with the following code:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
System.out.println(userService);
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

<!--    <import resource="引入其他bean xml配置文件" />-->
    <bean id="userService" class="org.example.spring.bean.UserService"/>
</beans>

It doesn’t matter if you are really unfamiliar, the following one may be relatively familiar: (I will also explain the Spring of this startup method later. In addition to the more mainstream ones below, it is also because the following method is more widely used, updated, and content relatively rich points! )

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
System.out.println(userService);
@Component
public class UserService {
    
    

    public void test() {
    
    
        System.out.println("这是一个测试方法");
    }
}

Haha, I guess many friends who have directly entered the era of Java [SpringBoot] may not have even seen the above.
What does the above code do? Very simple, just start a Spring container. The above two different startup methods are just different bean registration methods. For example, the former is defined by reading the tags xmlinside <bean>, and the latter is the read annotation Bean.
At this point, I would like to ask you a question, that is, through the code of the second method above, what did you find? My discovery is: I just call a line of code, and I can start using the Bean defined by Spring. I don't care about dependency injection, AOP, etc., just do it directly. What does this prove? In fact, it is superficial and a bit nonsense, and that is the proof:Through this line of code, it helps me complete all the basic capabilities of Spring that we usually use.

2. General process speculation

According to the Spring-related operations we have learned before, we can simply speculate on what is done in this line of code.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

2.1 Scanning

The first is the first point [scanning]. We have written so many beans in the project, or in other words, there are so many classes in my project, which are beans and which are ordinary classes, how does Spring recognize it? In fact, the reason is very simple. Spring is not that smart. If you want to get the information of this class, Spring must take a look [in person] to know the specific information of this class. How many files do you have, how many classes will he scan. The key code is as follows:

// 定义需要扫描的基础包名
@ComponentScan("org.tuling.spring")
public class AppConfig {
    
    
}

2.2 IOC

After scanning all the files, Spring can basically determine which are Beans and which are ordinary classes. Next, you can start to create beans, here is the so-called IOC process

2.3 AOP

AOP must have happened after IOC. If you understand the [proxy pattern] in the design pattern, it is not difficult to understand this. After all, if the target object is not fully functional, the proxy object's functionality will also be affected.

2.4 Summary

insert image description here

3. [Scanning] The process is simple to speculate

We also said before that scanning requires Spring to check in person which ones need to be created and which ones are not. Take our example new AnnotationConfigApplicationContext(AppConfig.class), the general steps are as follows: (simple speculation, unknown)

  1. It needs to look first AppConfig.class, read the base path of the scanned package
  2. According to the basic path read in the previous step, traverse all the files under the package, if there are annotations such as @Component, @Serviceetc. on the class, it is confirmed as a Bean
  3. After filtering, record the read Bean information, for example, save it in a Map for subsequent traversal

insert image description here

4. 【IOC】Simple guessing process

The IOC process, in fact, has a more professional term in Spring, called: Bean life cycle. A few simple words contain a lot of content. Before that, let's take a look at the [JVM object creation process] in [Pre-knowledge] to deepen your impression.
Including:

  1. Use the construction method of this class to instantiate an object (but if there are multiple construction methods in a class, Spring will choose, this is called the inferred construction method)
  2. After getting an object, Spring will judge whether there are attributes annotated by @Autowired in the object, find out these attributes and assign values ​​​​by Spring (dependency injection)
  3. After dependency injection, Spring will judge whether the object implements the BeanNameAware interface, BeanClassLoaderAware interface, and BeanFactoryAware interface. If it is implemented, 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 judge whether there is a method annotated by @PostConstruct in the object. If it exists, Spring will call this method of the current object (before initialization)
  5. Immediately afterwards, Spring will judge whether the object implements the InitializingBean interface. If it is implemented, it means that the current object must implement the afterPropertiesSet() method in the interface, and then Spring will call the afterPropertiesSet() method in the current object (initialization)
  6. Finally, Spring will judge whether the current object needs to perform AOP. If not, the Bean will be created. If AOP is required, it will perform dynamic proxy and generate a proxy object as a Bean (after initialization)

Another thing to note is that 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 the beanName, and the value is the Bean object. In this way, the next time you getBean, you can directly get the corresponding Bean object from the Map (in fact, 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 actions in the future, and no Map will be stored. The above creation process will be executed again the next time you getBean, and a new Bean object will be obtained.

insert image description here

4.1 Detailed description of the process of inferring the construction method

In the process of generating beans based on a certain class, Spring needs to use the construction method of the class to instantiate an object, but if a class has multiple construction methods, which one will Spring use?

Spring's judgment logic is as follows:

  1. If a class has only one constructor, regardless of whether the constructor has parameters or no parameters, Spring will use this constructor to create objects because there is no choice;
  2. If this class has multiple constructors:
    • If a no-argument constructor exists, use the no-argument constructor. Because in Java, the no-argument constructor itself has a default meaning in it;
    • If there is no no-argument constructor, then look at multiple no-argument constructors, which one has @Autowiredmodification, choose it; if there is no one, you can only report an error

still have a question. If Spring chooses a construction method with parameters, Spring needs to pass in parameters when calling this construction method with parameters, so how does this parameter come from? The answer is: Spring will find the Bean object in Spring according to the type and name of the input parameter.
3. First search according to the input parameter type, if only one is found, then use it directly as an input parameter;
4. If more than one is found according to the type, then determine the only one according to the input parameter name;
5. Finally, if not found, An error will be reported and the current Bean object cannot be created.

5. [AOP] simple guessing process

AOP is dynamic proxying. In the process of creating a Bean, Spring is in the last step (Before putting into the singleton pool) will judge whether the Bean currently being created needs to perform AOP, and if necessary, it will perform dynamic proxy.
So, how to judge whether a Bean needs to be proxied by AOP? Proceed as follows:

  1. Find all the facet beans (the facets are also from beans, or called: special beans)
  2. Traverse each method in the aspect to see if @Before, @After and other annotations (notifications) are written
  3. If it is written, judge 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, which means that AOP is required

The general process of AOP using cglib: (see the proxy paradigm above)

  1. Add a proxy class XxxProxy, which inherits from the proxy object XxxTarget, and holds a XxxTarget member variable (this member variable needs to go through a Bean declaration cycle, that is, complete IOC, etc.)
  2. Override the method of the parent class in the proxy class
  3. When executing the method of the proxy class, the method of the proxy class is called, but at the same time, the logic of the aspect needs to be executed

insert image description here

Then here is a paradigm for [proxy mode]:

// 被代理对象
public class ProxyTarget {
    
    
    public void run() {
    
    
        System.out.println("这是普通对象的run");
    }
}


// 代理对象
public class ProxyModel extends ProxyTarget {
    
    
    private ProxyTarget proxyTarget;

    public void setProxyTarget(ProxyTarget proxyTarget) {
    
    
        this.proxyTarget = proxyTarget;
    }

    @Override
    public void run() {
    
    
        System.out.println("我代理对象可以在这里做加强---1");
        super.run();
        System.out.println("我代理对象也可以在这里做加强---2");
    }
}

Six, Spring affairs

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

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

Criteria for judging whether a Spring transaction will fail: When a method annotated with @Transactional is called, it is necessary to judge whether it is directly called by the proxy object. If so, the transaction will take effect, and if not, it will fail.(PS: This point is easy to be overlooked)
In addition, there is another classic example, that is , the result is different when @Beanthere is or is not , as follows:@Configuration

The way to declare the bean:

@ComponentScan("org.tuling.spring")
@Configuration
public class AppConfig {
    
    
    
    @Bean
    public UserService userService() {
    
    
        return new UserService(walletService());
    }

    @Bean
    public UserService userService1() {
    
    
        return new UserService(walletService());
    }

    @Bean
    public WalletService walletService() {
    
    
        return new WalletService();
    }
}

// UserService声明
public class UserService {
    
    
    private WalletService walletService;

    public UserService() {
    
    }
    public UserService(WalletService walletService) {
    
    
        this.walletService = walletService;
    }

    public WalletService getWalletService() {
    
    
        return walletService;
    }

    /**
     * 自我介绍
     */
    public void selfIntroduction() {
    
    
        System.out.println("你好,我是阿通,我有好多钱");
        walletService.showMyBalance();
    }
}

Look at the method of declaring Bean above. According to the assumption, WalletServiceit must be a singleton, so it must be the same userServiceas the object userService1held .walletService

Call method:

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        UserService userService = (UserService)context.getBean("userService");
        System.out.println(userService);
        System.out.println(userService.getWalletService());


        System.out.println("--------------------------------");
        UserService userService1 = (UserService)context.getBean("userService1");
        System.out.println(userService1);
        System.out.println(userService1.getWalletService());

The resulting output is as follows:

org.tuling.spring.bean.UserService@2c34f934
org.tuling.spring.bean.WalletService@12d3a4e9
--------------------------------
org.tuling.spring.bean.UserService@240237d2
org.tuling.spring.bean.WalletService@12d3a4e9

Looking at the results, there is no problem, and the output is as scheduled. But if we remove the method that declares the Bean @Configuration, the result will become like this:

@ComponentScan("org.tuling.spring")
//@Configuration
public class AppConfig {
    
    

    @Bean
    public UserService userService() {
    
    
        return new UserService(walletService());
    }

    @Bean
    public UserService userService1() {
    
    
        return new UserService(walletService());
    }

    @Bean
    public WalletService walletService() {
    
    
        return new WalletService();
    }
}
org.tuling.spring.bean.UserService@710726a3
org.tuling.spring.bean.WalletService@646007f4
--------------------------------
org.tuling.spring.bean.UserService@481a15ff
org.tuling.spring.bean.WalletService@78186a70

Why, just simply annotating a @Configurationresult is different? analyse as below:

  1. @BeanThe annotation can register the object returned by the method as a Bean, and the Bean will be managed by the Spring container. nothing more
  2. Therefore, when the userService()method is called again walletService(), it is actually just an ordinary Java call, and it will definitely be called again.new WalletService()
  3. After being @Configurationannotated, all methods will be proxied (the source code evidence has not been found yet, and I will attach it later when I understand it)

summarize

  1. Simply learned the process of Spring startup
  2. Through the series of some common Spring operations, I have a general understanding of the general process of IOC and AOP

Guess you like

Origin blog.csdn.net/qq_32681589/article/details/132101489