1. Spring5 source code-IOC container design concept and the role of core annotations

What can be learned?

0. Spring overall context
1. Describe BeanFactory
2. Difference between BeanFactory and ApplicationContext
3. Briefly describe the loading process of SpringIoC
4. Briefly describe the life cycle of Bean
5. What extension interfaces and calling mechanisms are available in Spring

1. Introduction to the overall context of spring source code and source code compilation

 1.1. What is IOC

ioc is the inversion of control, which is a design concept used to solve the coupling problem between layers and between classes.

For example, now there are two classes A and B, and class B is referenced in class A. So if one day, class B is to be replaced, what will we do? If class B is referenced 100 times, we need to replace 100 times?

Now, A calls B directly. If we call B indirectly, wrap B. If we change B to C in the future, we only need to replace it in the wrapper class. We don’t need to modify class A. This is the control Reverse.

Spring uses ioc, Spring.ioc(A, B) stores the references of A and B in the ioc, spring will help us maintain it, so there is no need to worry.

When we want to use B in A, use the interface corresponding to B, and then use the @Autowired annotation

A {
   @Autowired
   private IB b;  
}

When to replace B, it doesn't hurt or itchy, just put the new class in IoC.

1.2. The overall context of Spring source code

Spring IoC is a container, and many Beans are maintained in Spring IoC

So how are these beans registered in IoC? In other words, how is our custom class managed as a bean by the IoC container?

Let me recall first, the steps we took when developing spring: 

第一步: 配置类. 配置类可以使用的方式通常由 
1) xml配置 
2) 注解配置 
3) javaconfig方式配置

第二步: 加载spring上下文 
1) 如果是xml, 则new ClassPathXmlApplicationContext("xml"); 
2) 如果是注解配置: 则new AnnotationConfigApplicationContext(config.class)

第三步: getBean() 
我们会讲自定义的类, 通过xml或者注解的方式注入到ioc容器中.</pre>

In this step, the classes specified in the xml or annotations will be injected into the IoC container. 

1.2.1 So, how exactly do you inject a class into ioc? 

Let's sort out the whole process below. 

First question: What is the most important and core class to produce a class into a bean?

Is BeanFactory

Question 2: What is BeanFactory?

BeanFactory is the core interface at the top level of Spring-using the simple factory pattern. Usually an instance is produced based on a name, and the bean object is obtained according to the unique identifier passed in, but it is specifically created after passing in the parameters, or passing in the parameters Create before, this depends on the specific situation, according to the name or type to produce different beans. 

One sentence summary: BeanFactory's responsibility is to produce Bean

Consider the following code: 

public static void main( String[] args ) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getBean("***");
    }

The function implemented by this code is to read the files in the directory where the current file is located and its subdirectories, and then get the bean with the specified name. The whole process is shown in the following figure: 

image

首先, 通过ClassPathXmlApplicationContext或者AnnotationConfigApplicationContext去读取配置,

然后, 将其交给BeanFactory.

第三. BeanFactory调用getBean()方法, 将Bean注入到IoC容器中**</pre>

We found that the configuration reading may be xml or annotation.  Different reading methods should use different tools . Then the results of these tools should be unified before they can be handed over to BeanFactory To deal with.

Because these similarities and differences are not processed in  BeanFactory . BeanFactory has only one role , which is a production bean

1.2.2 So, how are the read configurations of different tools unified?

We know that there should be a different implementation for reading configuration. Read xml and annotation methods into a unified thing and put it into beanFactory. Who is this thing? It is BeanDefinition (Bean Definition) 

What does it mean? As shown below:

image

 Look at the part framed by the green box. This meaning is: through different tools, may be xmlApplicationContext, may be the configuration read by the annotationApplicationContext tool, and finally will be constructed into a BeanDefinition object. Then the BeanDefinition is passed to the BeanFactory, and the BeanFactory uniformly processes the BeanDefinition object and calls getBean() method, put it in the IoC container.

1.2.3 So how is the read configuration unified and structured into a BeanDefinition?

Let’s take an example. Now there is a person. For example, I just bought a house and I want to decorate. I need a wardrobe. At this time, I will find a wardrobe store. Then tell him my needs, the color of the cabinet, the style format What kind of. Then the wardrobe store records my needs. At this time, he will not produce by himself, he will notify the factory and let the factory produce it. According to what the factory produces, the wardrobe store has a designer, and their designer will follow I design a drawing for my needs. Then I hand it over to the factory. The factory produces Beans according to the drawings. 

The whole process is as follows:

image

 The entrance is "I"

1. I have a need, open a cabinet and find a wardrobe store

2. I tell the wardrobe store my needs, the color and style of the cabinet, and the designer of the wardrobe store designs a drawing according to my requirements

3. The wardrobe shop sends the drawings to the factory, and the factory produces cabinets according to the drawings

This is the process of making a wardrobe. When drawing a drawing, one drawing is given to the factory. This is too inefficient. We can draw n pieces and give it to the factory together. So, in the design drawing, there is one Container to store multiple drawings

Later, if I want to customize a cabinet shop. Then, tell the designer the color and style of my cabinet, it's fine. The process is the same as above. 

The whole process is similar to our bean production process

1. Define a class with @Component annotation, I found the wardrobe store, the wardrobe store is similar to the ApplicationContext.

2. I told ApplicationContext my needs, I want to lazily load @Lazy, set singleton mode or multi-case mode @Scope. The corresponding is to customize the color and style of the cabinet. Then the designer BeanDefinitionRegistry in the wardrobe store designs according to my needs The drawing, which is constructed into BeanDefinition. Different BeanDefinitionRegistry designs different BeanDefinition, and then put them all in the container.

3. The ApplicationContext of the wardrobe store uniformly delivers a stack of drawings BeanDefinitionMap to the factory, and then the factory produces Beans as required, and then puts the generated beans into the IoC container.

This is a process in which a class with @Component is loaded. 

If the wardrobe store wants to have a good business, then he has to go live, so it needs good sales. Sales have to scan the real estate, to contact, who has the need for decoration. Ask one by one. 

But I asked 100 people, maybe only 10 people have decoration needs. So there is also a reception, this reception has to contact customers, see who are interested customers, and filter them out. Then customize the furniture.

There are two more types of people here: sales and reception. The specific tasks are as follows.

image

Sales is equivalent to our BeanDefinitionReader. His role is to scan real estate and find potential customers. The corresponding is BeanDefinitionReader to read xml configuration or Annotation annotations. 

There are many configurations and annotations in xml, which are not all our goals. So there is a reception

The reception must scan all potential customers. Scan out the interested customers. This is similar to our BeanDefinitionScanner, to scan potential customers, and finally filter out the classes annotated with @Component

This is the customer who needs custom furniture later

BeanDefinitionReader对应的就去读取配置类, 看看有哪些需求需要搞装修.

它本身也是一个抽象类, 可以看到他有AnnotationBeanDefinitionReader和XmlBeanDefinitionReader

我们配置了配置包, 去扫描这个包下所有的类, **然后将扫描到的所有的类交给BeanDefinitionScanner, 它会去过滤带有@Component的类.** </pre>

In connection with the above process, it is the process where the entire configuration file is loaded into the IoC. 

1.3. The difference between ApplicationContext and FactoryBean

1. The function of FactoryBean is to produce beans. It produces beans based on BeanDefinition. Therefore, only one can be produced at a time

2. There are two kinds of ApplicationContext. One is xmlApplicationContext and the other is annotationApplicationContext. The parameter he passes in is a configuration file. That is, all classes with @Component in a directory can be loaded

Both of them have their own usage scenarios. Most of them use ApplicationContext.

Another difference: As I will talk about later, ApplicationContext has two extension interfaces that can be used to integrate with outsiders. For example, integration with MyBatis.

1.4. Bean life cycle

image

As shown in the figure above, the beanFactory gets the BeanDefinition and directly calls getBean() to produce the Bean? 

No, there is a process for producing Bean. Let's take a look at the life cycle of Bean

第一步: 实例化. bean实例化的时候从BeanDefinition中得到Bean的名字, 然后通过反射机制, 将Bean实例化. 实例化以后, 这是还只是个壳子, 里面什么都没有.

第二步: 填充属性. 经过初始化以后, bean的壳子就有了, bean里面有哪些属性呢? 在这一步填充

第三步: 初始化. 初始化的时候, 会调用initMethod()初始化方法, destory()初始化结束方法

这个时候, 类就被构造好了.

第四步: 构造好了的类, 会被放到IoC的一个Map中. Map的key是beanName, value是bean实例. 这个Map是一个单例池, 也就是我们说的一级缓存

第五步: 我们就可以通过getBean("user"), 从单例池中获取雷鸣是user的类了.

 In the process of constructing the bean, there will be many detailed problems, such as circular dependencies.

 image

 Class B is called in class A, so BeanFactory will construct B when constructing A. Then when constructing B, it is found that B also depends on A. In this way, it is a circular dependency. This is not possible. 

How does Spring solve the problem of circular dependencies? 

Set the exit. For example, A is in the process of constructing, then a mark is set, and it is being constructed. Then B is constructed, and A is applied during the construction process. At this time, it is interesting to construct A, and then find that A is being constructed, then, Then A will not be constructed again. 

Later, I will explain in detail how Spring solves circular references. What we need to know here is:  Spring uses a three-level cache to solve the problem of circular references

 In fact, the bean is stored in the first-level cache, and the circular reference is solved by the third-level cache. In fact, the first, second, and third-level cache are Maps.

1.5. Extension interface in Spring

There are two very important extension interfaces. BeanFactoryPostProcessor (the post processor of the Bean factory) and BeanDefinitionRegistryPostProcessor

What do these two interfaces do? 

image

In this picture, we see that the designer wants to design a drawing, and then hand the drawing to the factory for production. Then, is the drawing designed by the designer likely to be modified?

Of course it can be modified. As long as it has not been handed over to the factory, it can be modified.

The role of BeanFactoryPostProcessor (the post processor of the Bean factory) is to modify the BeanDefinition.

1. BeanFactoryPostProcessor: Modify BeanDefinition.

It is an interface, our class can implement this interface, and then rewrite the methods inside

public class DefinedPost implements BeanFactoryPostProcessor {
    /**
     * 重写Bean工厂的后置处理器
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // beanFactory 拿到工厂了, 就可以获取某一个Bean定义了
        GenericBeanDefinition car = (GenericBeanDefinition) beanFactory.getBeanDefinition("Car");
        // 拿到了car, 然后修改了Car的类名为com.example.tulingcourse.Tank. 那么后面在获取的Bean里面, 将其转换为Car, 就会报错了
        car.setBeanClassName("com.example.tulingcourse.Tank");
    }
}

Step 1: Implement the BeanFactoryPostProcessor interface, and then need to rewrite the methods inside

Step 2: We found that the rewriting method directly gave us beanFactory, bean factory

Step 3: Get the bean factory, we can get the BeanDefinition based on the name, which is the bean definition. 

Step 4: We modified the class name in the bean definition to Tank. 

What will happen at this time? The car constructed from the bean factory will be converted into a Car object after being taken out, and an error will be reported.

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
    
        Car car = context.getBean("car", Car.class); // 这里会报错, 因为已经被修改
        System.out.println(car.getName());
    }

Execution process: When spring starts, it will execute AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TulingCourseApplication.class);

Then the ApplicationContext goes back to scan all classes that implement the BeanFactoryPostProcessor object, and then executes the postProcessBeanFactory method. 

BeanFactoryPostProcessor is used in many scenarios, when integrating other components, such as integrating mybatis

2. BeanDefinitionRegistryPostProcessor registered BeanDefinition

This is a post processor for Bean definition registration. BeanDefinitionRegistryPostProcessor is actually implementing the BeanFactoryPostProcessor interface

image

 Let's look at a demo

public class DefinedPost implements BeanDefinitionRegistryPostProcessor {

    /**
     * 重写Bean工厂的后置处理器
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // beanFactory 拿到工厂了, 就可以获取某一个Bean定义了
        GenericBeanDefinition car = (GenericBeanDefinition) beanFactory.getBeanDefinition("Car");
        // 拿到了car, 然后修改了Car的类名为com.example.tulingcourse.Tank. 那么后面在获取的Bean里面, 将其转换为Car, 就会报错了
        car.setBeanClassName("com.example.tulingcourse.Tank");
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {

    }
}

A class implements BeanDefinitionRegistryPostProcessor and needs to override the postProcessBeanDefinitionRegistry method, which gives us the BeanDefinitionRegistry directly. 

Then use beanDefinitionRegistry.registerBeanDefinition(); to add drawings

Here you can register new beans, or delete the registered beans. If you register one more, the bean factory will build one more. 

to sum up:

    The two extension classes BeanFactoryPostProcessor and BeanDefinitionRegistryPostProcessor are very important classes, which play a great role in extending externally, such as: integrating mybatis

image

BeanFactoryPostProcessor and BeanDefinitionRegistryPostProcessor are two extension interfaces in ApplicationContext. This is also one of the differences between ApplicationContext and BeanFactory, because with these two extension nodes, it can be integrated with the outside. For example, Mybatis integration. For example: Scan configuration class, which is It is achieved through  these two extension points.

The role of this extension point:

1. In addition to IoC, other extensions, such as AOP, and MyBatis integration, all use these two extension points. The reason why Spring can be integrated with many external components in an orderly and undisrupted manner is the function of these two extension points

1.6 Bean extension points

In addition to ApplicationContext has extension points, beans in Spring IoC also have extension points. BeanPostProcessor (Bean's post processor). If used before getBean(), you can prevent the construction of the Bean, and you can also customize the construction of the Bean.

BeanPostProcessor is used in many scenarios. It will be called before and after the bean is instantiated. It will also be called before and after the properties are filled, and it will also be called before and after initialization. Some procedures are called more than once. The entire process will be called 9 times in total . Bean can be extended in every process.

Thinking: How did Spring join AOP?

Integrated AOP will definitely not be integrated with IoC. AOP is integrated through BeanPostProcessor (Bean post processor).

There are two ways to implement AOP: one is CGLIB, and the other is JDK. 

If you want to integrate, at which step will it be inherited? For example, to add logs, use AOP to add. We usually add AOP after initialization. AOP is integrated here.

As shown above: When interviewing, the interviewer asks you, the life cycle of Bean, we can't just talk about instantiation-->filling properties-->initialization. We also need to talk about initialization when there are a series of aware.

1.7. Spring IOC loading process

image

Compared with the above figure, let's briefly describe the loading process of ioc 

We load a class into a Bean, not in one step, we need to go through the process. 

1. First, we have to load the class as BeanDefinition (Bean Definition)

  To load into a bean definition, there are several steps:

  1) Use BeanDefinitionReader to load the configuration class. At this time, it scans all the xml files or the annotations in the project. Some of these are our target classes, and some are not

  2) Use BeanDefinitionScanner to scan out our target class. 

  3) Use BeanDefinitionRegistry to register beans in BeanDefinitionMap.

2. Then, ApplicationContext can call BeanFactoryPostProcessor to modify the bean definition, and it can also call BeanDefinitionRegistryPostProcessor to register the bean definition

3. The BeanDefinition is handed over to the BeanFactory for processing, and the BeanFactory calls getBean() to generate Beans or call Beans (getBean() has two functions). 

4. When the bean is produced, it will be instantiated first, and then the properties will be filled (mainly to read @Autowire, @Value and other annotations). When the bean is initialized, the initMethod() method and the initialization destruction method destroy() will be called. Initialization A bunch of Aware will also be called when, and there will be many extension points during the bean generation process for us to extend.

5. Put the produced Bean into the Map. The map is a first-level cache pool. Later, we can get the bean from the cache pool through getBean("user")

Guess you like

Origin blog.51cto.com/15091061/2609370