Detailed explanation of Spring Bean life cycle

There are 13 links in the Spring bean life cycle

  1. Stage 1: Bean meta information configuration stage

  2. Stage 2: Bean meta information parsing stage

  3. Phase 3: register the Bean into the container

  4. Phase 4: BeanDefinition merge phase

  5. Phase 5: Bean Class loading phase

  6. Stage 6: Bean instantiation stage (2 small stages)

    • Pre instantiation stage of Bean

    • Bean instantiation phase

  7. Stage 7: BeanDefinition processing after merging

  8. Stage 8: attribute assignment stage (3 small stages)

    • Post instantiation stage of Bean

    • Stage before Bean attribute assignment

    • Bean attribute assignment stage

  9. Stage 9: Bean initialization stage (5 small stages)

    • Bean Aware interface callback phase

    • Pre Bean initialization phase

    • Bean initialization phase

    • Post Bean initialization phase

  10. Stage 10: the stage after initialization of all singleton bean s is completed

  11. Phase 11: Bean usage phase

  12. Stage 12: pre Bean destruction stage

  13. Stage 13: Bean destruction stage

 

Stage 1: Bean meta information configuration stage

This stage is mainly the definition stage of bean information.

 

There are four ways to define Bean information

  • API approach

  • Xml file mode

  • properties file

  • Annotation method

 

API approach

Let's start with this method, because several other methods will eventually use this method to define bean configuration information.

When the Spring container is started, the Bean will be parsed into the BeanDefinition structure inside Spring. Whether it is through the < Bean > tag of the xml configuration file, or the @ Bean configured through annotation, or the class marked by @ compound, or the scanned class, it will eventually be resolved into a BeanDefinition object. Finally, our Bean factory will instantiate and initialize the Bean according to the definition information of the Bean.

You can throw the BeanDefinition to the Bean factory, and then the Bean factory will help you produce a Bean instance based on this information and use it.

BeanDefinition contains various information of bean definition, such as class, scope, lazy information, dependOn information, autowireCandidate (whether it is a candidate), primary (whether it is a main candidate) and other information.

BeanDefinition is an interface. There are several implementation classes. Take a look at the class diagram:

BeanDefinition interface: bean definition information interface

The interface representing bean definition information defines various methods to obtain bean definition configuration information. Let's take a look at the source code:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
​
    /**
     * Set the parent bean name of this bean (corresponding to the parent attribute of the bean element in xml)
     */
    void setParentName(@Nullable String parentName);
​
    /**
     * Returns the name of the parent bean specified when defining this bean
     */
    @Nullable
    String getParentName();
​
    /**
     * Specify the bean class name of this bean definition (corresponding to the class attribute of the bean element in xml)
     */
    void setBeanClassName(@Nullable String beanClassName);
​
    /**
     * Returns the current bean class name of this bean definition
     * Note that if a child definition overrides / inherits the class name of its parent class, this is not necessarily the actual class name used at runtime. In addition, this may only be the class that calls the factory method, or it may even be empty in the case of the factory bean reference that calls the method. Therefore, do not think of this as the final bean type of the runtime, but only use it for resolution purposes at the individual bean definition level.
     */
    @Nullable
    String getBeanClassName();
​
    /**
     * Set the life cycle of this bean, such as singleton and prototype (corresponding to the scope attribute of the bean element in xml)
     */
    void setScope(@Nullable String scope);
​
    /**
     * Return the life cycle of this bean, such as singleton and prototype
     */
    @Nullable
    String getScope();
​
    /**
     * Set whether the initialization of this bean should be delayed (corresponding to the lazy attribute of the bean element in xml)
     */
    void setLazyInit(boolean lazyInit);
​
    /**
     * Returns whether to delay the initialization of this bean, which is only valid for single instance beans
     */
    boolean isLazyInit();
​
    /**
     * Setting this bean depends on the name of the initialized bean. The bean factory will ensure that the bean specified by dependsOn will be initialized before the current bean is initialized
     */
    void setDependsOn(@Nullable String... dependsOn);
​
    /**
     * Returns the name of the bean on which this bean depends
     */
    @Nullable
    String[] getDependsOn();
​
    /**
     * Set whether this bean is a candidate for automatic injection of other beans
     * autowireCandidate
     */
    void setAutowireCandidate(boolean autowireCandidate);
​
    /**
     * Returns whether this bean is a candidate for automatic injection of other beans
     */
    boolean isAutowireCandidate();
​
    /**
     * Set whether this bean is the primary candidate for automatic injection
     * primary: Is it the primary candidate
     */
    void setPrimary(boolean primary);
​
    /**
     * Returns whether this bean is the primary candidate for automatic injection
     */
    boolean isPrimary();
​
    /**
     * Specify the factory bean to use, if any. This is the name of the bean on which you want to call the specified factory method.
     * factoryBeanName: Factory bean name
     */
    void setFactoryBeanName(@Nullable String factoryBeanName);
​
    /**
     * Return the factory bean name (if any) (corresponding to the factory bean attribute of the bean element in xml)
     */
    @Nullable
    String getFactoryBeanName();
​
    /**
     * Specify the factory method, if any. This method will be called with constructor parameters, and if no parameters are specified, it will not be called with any parameters. This method will be called on the specified factory bean (if any) or as a static method on the local bean class.
     * factoryMethodName: Factory method name
     */
    void setFactoryMethodName(@Nullable String factoryMethodName);
​
    /**
     * Return the factory method name (corresponding to the factory method attribute of the bean in xml)
     */
    @Nullable
    String getFactoryMethodName();
​
    /**
     * Returns the constructor parameter value of this bean
     */
    ConstructorArgumentValues getConstructorArgumentValues();
​
    /**
     * Whether there is constructor parameter value setting information (corresponding to < constructor Arg / > child elements of bean elements in xml)
     */
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }
​
    /**
     * Get the attribute value setting information of bean definition configuration
     */
    MutablePropertyValues getPropertyValues();
​
    /**
     * Whether there is property setting information in the bean definition (corresponding to the < property / > child element of the bean element in xml)
     */
    default boolean hasPropertyValues() {
        return !getPropertyValues().isEmpty();
    }
​
    /**
     * Set bean initialization method name
     */
    void setInitMethodName(@Nullable String initMethodName);
​
    /**
     * bean Initialization method name
     */
    @Nullable
    String getInitMethodName();
​
    /**
     * Set the name of the bean destruction method
     */
    void setDestroyMethodName(@Nullable String destroyMethodName);
​
    /**
     * bean Method name of destruction
     */
    @Nullable
    String getDestroyMethodName();
​
    /**
     * Set the role information of the bean
     */
    void setRole(int role);
​
    /**
     * bean Defined role information
     */
    int getRole();
​
    /**
     * Set bean description information
     */
    void setDescription(@Nullable String description);
​
    /**
     * bean Description information
     */
    @Nullable
    String getDescription();
​
    /**
     * bean Type parser
     */
    ResolvableType getResolvableType();
​
    /**
     * Is it a singleton bean
     */
    boolean isSingleton();
​
    /**
     * Is it a multi column bean
     */
    boolean isPrototype();
​
    /**
     * The abstract attribute corresponding to the bean element in xml is used to specify whether it is abstract
     */
    boolean isAbstract();
​
    /**
     * Returns a description of the resource from which this bean definition comes (to display the context in case of an error)
     */
    @Nullable
    String getResourceDescription();
​
    @Nullable
    BeanDefinition getOriginatingBeanDefinition();
​
}

The BeanDefinition interface also inherits two interfaces:

  • AttributeAccessor

  • BeanMetadataElement

AttributeAccessor interface: attribute provider interface

public interface AttributeAccessor {
​
    /**
     * Set attribute - > value
     */
    void setAttribute(String name, @Nullable Object value);
​
    /**
     * Get the value corresponding to a property
     */
    @Nullable
    Object getAttribute(String name);
​
    /**
     * Remove an attribute
     */
    @Nullable
    Object removeAttribute(String name);
​
    /**
     * Whether to include an attribute
     */
    boolean hasAttribute(String name);
​
    /**
     * Returns all property names
     */
    String[] attributeNames();
​
}

This interface is equivalent to an operation of key - > value data structure. BeanDefinition inherits this. In fact, LinkedHashMap is used internally to implement all methods in this interface. Usually, we use these methods to save some additional information generated in the process of BeanDefinition definition.

BeanMetadataElement interface

Take a look at its source code:

public interface BeanMetadataElement {
​
    @Nullable
    default Object getSource() {
        return null;
    }
​
}

BeanDefinition inherits this interface, and getSource returns the source of BeanDefinition definition. For example, if we define BeanDefinition through xml, then getSource represents the xml resource that defines the bean; If we define BeanDefinition through api, we can set source as the class where BeanDefinition is defined. When an error occurs, we can easily troubleshoot it according to this source.

RootBeanDefinition class: represents the root bean definition information

This representation is usually used when there is no parent bean in the bean

ChildBeanDefinition class: represents the sub bean definition information

If you need to specify the parent bean, you can use ChildBeanDefinition to define the configuration information of the child bean, which has a parentName attribute to specify the name of the parent bean.

GenericBeanDefinition class: General bean definition information

It can represent the bean configuration information without the parent bean or the child bean configuration information with the parent bean. This class also has the parentName attribute to specify the name of the parent bean.

ConfigurationClassBeanDefinition class: refers to the bean information defined through the @ bean method in the configuration class

You can label some methods by using @ bean in the configuration class, and define beans through these methods. The bean information configured by these methods will finally be converted into an object of type ConfigurationClassBeanDefinition

AnnotatedBeanDefinition interface: represents the bean information defined by annotation

There's a way

AnnotationMetadata getMetadata();

Used to get all annotation information on the class that defines this bean.

BeanDefinitionBuilder: tool class for building BeanDefinition

In order to facilitate the operation of BeanDefinition in spring, a class is provided: BeanDefinitionBuilder, which internally provides many static methods. Through these methods, it is very convenient to assemble BeanDefinition objects. Let's experience it through a case.

Case 1: assemble a simple bean

Let's have a simple class

package com.javacode2018.lesson002.demo1;
​
public class Car {
    private String name;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                '}';
    }
}

test case

@Test
public void test1() {
    //Specify class
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName());
    //Get BeanDefinition
    BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    System.out.println(beanDefinition);
}

Equivalent to

<bean class="com.javacode2018.lesson002.demo1.Car" />

Run output

Root bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null

Case 2: assembling a bean with attributes

code

@Test
public void test2() {
    //Specify class
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName());
    //Set common type properties
    beanDefinitionBuilder.addPropertyValue("name", "audi"); //@1
    //Get BeanDefinition
    BeanDefinition carBeanDefinition = beanDefinitionBuilder.getBeanDefinition();
    System.out.println(carBeanDefinition);
​
    //Create spring container
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); //@2
    //Call registerBeanDefinition to register the bean in the container
    factory.registerBeanDefinition("car", carBeanDefinition); //@3
    Car bean = factory.getBean("car", Car.class); //@4
    System.out.println(bean);
}

@1: Call addPropertyValue to set the value of name in the Car

@2: Created a spring container

@3: Register the bean configuration information of carBeanDefinition into the spring container. The name of the bean is car

@4: Get the bean car from the container, and finally output it

Run output

Root bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Car{name='audi'}

The second line outputs the car bean instance object obtained from the container.

Case 3: assembling a dependent bean

Another class

There is a car attribute in the following class. We inject this attribute through spring.

package com.javacode2018.lesson002.demo1;

public class User {
    private String name;

    private Car car;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", car=" + car +
                '}';
    }
}

Key code

@Test
public void test3() {
    //First create the BeanDefinition of car
    BeanDefinition carBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName()).addPropertyValue("name", "audi").getBeanDefinition();
    //Create User BeanDefinition
    BeanDefinition userBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class.getName()).
            addPropertyValue("name", "Passerby a Java").
            addPropertyReference("car", "car"). //@1
            getBeanDefinition();

    //Create spring container
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    //Call registerBeanDefinition to register the bean in the container
    factory.registerBeanDefinition("car", carBeanDefinition); 
    factory.registerBeanDefinition("user", userBeanDefinition);
    System.out.println(factory.getBean("car"));
    System.out.println(factory.getBean("user"));
}

@1: To inject a dependent bean, you need to use the addPropertyReference method with two parameters. The first is the name of the property and the second is the name of the bean to be injected

The above code is equivalent to

<bean id="car" class="com.javacode2018.lesson002.demo1.Car">
    <property name="name" value="audi"/>
</bean>

<bean id="user" class="com.javacode2018.lesson002.demo1.User">
    <property name="name" value="Passerby a Java"/>
    <property name="car" ref="car"/>
</bean>

Run output

Car{name='audi'}
User{name='Passerby a Java', car=Car{name='audi'}}

Case 4: two bean s with parent-child relationship

@Test
public void test4() {
    //Create this bean car definition first
    BeanDefinition carBeanDefinition1 = BeanDefinitionBuilder.
            genericBeanDefinition(Car.class).
            addPropertyValue("name", "Porsche").
            getBeanDefinition();

    BeanDefinition carBeanDefinition2 = BeanDefinitionBuilder.
            genericBeanDefinition(). //Generate a GenericBeanDefinition object internally
            setParentName("car1"). //@1: Set the name of the parent bean to car1
            getBeanDefinition();

    //Create spring container
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    //Call registerBeanDefinition to register the bean in the container
    //Register Car1 - > carbeandefinition1
    factory.registerBeanDefinition("car1", carBeanDefinition1);
    //Register car2 - > carbeandefinition2
    factory.registerBeanDefinition("car2", carBeanDefinition2);
    //Get car1 from container
    System.out.println(String.format("car1->%s", factory.getBean("car1")));
    //Get car2 from container
    System.out.println(String.format("car2->%s", factory.getBean("car2")));
}

Equivalent to

<bean id="car1" class="com.javacode2018.lesson002.demo1.Car">
    <property name="name" value="Porsche"/>
</bean>
<bean id="car2" parent="car1" />

Run output

car1->Car{name='Porsche'}
car2->Car{name='Porsche'}

Case 5: setting (Map, Set, List) attributes through api

Let's demonstrate the injection of List, Map and Set. The internal elements are common types and other bean elements.

Let's have a class

package com.javacode2018.lesson002.demo1;
​
import java.util.List;
import java.util.Map;
import java.util.Set;
​
public class CompositeObj {
​
    private String name;
    private Integer salary;
​
    private Car car1;
    private List<String> stringList;
    private List<Car> carList;
​
    private Set<String> stringSet;
    private Set<Car> carSet;
​
    private Map<String, String> stringMap;
    private Map<String, Car> stringCarMap;
​
    //get and set methods are omitted here. Remember to fill them in when you write
​
    @Override
    public String toString() {
        return "CompositeObj{" +
                "name='" + name + '\'' +
                "\n\t\t\t, salary=" + salary +
                "\n\t\t\t, car1=" + car1 +
                "\n\t\t\t, stringList=" + stringList +
                "\n\t\t\t, carList=" + carList +
                "\n\t\t\t, stringSet=" + stringSet +
                "\n\t\t\t, carSet=" + carSet +
                "\n\t\t\t, stringMap=" + stringMap +
                "\n\t\t\t, stringCarMap=" + stringCarMap +
                '}';
    }
}

Note: the get and set methods are omitted above. Remember to fill them in when you write

First define a CompositeObj bean with xml, as follows

<?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">
​
    <bean id="car1" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="audi"/>
    </bean>
​
    <bean id="car2" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="Porsche"/>
    </bean>
​
    <bean id="compositeObj" class="com.javacode2018.lesson002.demo1.CompositeObj">
        <property name="name" value="Passerby a Java"/>
        <property name="salary" value="50000"/>
        <property name="car1" ref="car1"/>
        <property name="stringList">
            <list>
                <value>java High concurrency series</value>
                <value>mysql series</value>
                <value>maven Master Series</value>
            </list>
        </property>
        <property name="carList">
            <list>
                <ref bean="car1"/>
                <ref bean="car2"/>
            </list>
        </property>
        <property name="stringSet">
            <set>
                <value>java High concurrency series</value>
                <value>mysql series</value>
                <value>maven Master Series</value>
            </set>
        </property>
        <property name="carSet">
            <set>
                <ref bean="car1"/>
                <ref bean="car2"/>
            </set>
        </property>
        <property name="stringMap">
            <map>
                <entry key="Series 1" value="java High concurrency series"/>
                <entry key="Series 2" value="Maven Master Series"/>
                <entry key="Series 3" value="mysql series"/>
            </map>
        </property>
        <property name="stringCarMap">
            <map>
                <entry key="car1" value-ref="car1"/>
                <entry key="car2" value-ref="car2"/>
            </map>
        </property>
    </bean>
</beans>

Next, we implement it in a pure api way, as follows:

@Test
public void test5() {
    //Define car1
    BeanDefinition car1 = BeanDefinitionBuilder.
            genericBeanDefinition(Car.class).
            addPropertyValue("name", "audi").
            getBeanDefinition();
    //Define car2
    BeanDefinition car2 = BeanDefinitionBuilder.
            genericBeanDefinition(Car.class).
            addPropertyValue("name", "Porsche").
            getBeanDefinition();
​
    //Define the bean CompositeObj
    //Create the value corresponding to the property stringList
    ManagedList<String> stringList = new ManagedList<>();
    stringList.addAll(Arrays.asList("java High concurrency series", "mysql series", "maven Master Series"));
​
    //Create the value corresponding to the carList attribute, and internally reference the names of the other two bean s [car1,car2]
    ManagedList<RuntimeBeanReference> carList = new ManagedList<>();
    carList.add(new RuntimeBeanReference("car1"));
    carList.add(new RuntimeBeanReference("car2"));
​
    //Create the value corresponding to the property stringList
    ManagedSet<String> stringSet = new ManagedSet<>();
    stringSet.addAll(Arrays.asList("java High concurrency series", "mysql series", "maven Master Series"));
​
    //Create the value corresponding to the carSet attribute, and internally reference the names of the other two bean s [car1,car2]
    ManagedList<RuntimeBeanReference> carSet = new ManagedList<>();
    carSet.add(new RuntimeBeanReference("car1"));
    carSet.add(new RuntimeBeanReference("car2"));
​
    //Create the value corresponding to the property stringMap
    ManagedMap<String, String> stringMap = new ManagedMap<>();
    stringMap.put("Series 1", "java High concurrency series");
    stringMap.put("Series 2", "Maven Master Series");
    stringMap.put("Series 3", "mysql series");
​
    ManagedMap<String, RuntimeBeanReference> stringCarMap = new ManagedMap<>();
    stringCarMap.put("car1", new RuntimeBeanReference("car1"));
    stringCarMap.put("car2", new RuntimeBeanReference("car2"));
​
​
    //Next, we use the native api to create BeanDefinition
    GenericBeanDefinition compositeObj = new GenericBeanDefinition();
    compositeObj.setBeanClassName(CompositeObj.class.getName());
    compositeObj.getPropertyValues().add("name", "Passerby a Java").
            add("salary", 50000).
            add("car1", new RuntimeBeanReference("car1")).
            add("stringList", stringList).
            add("carList", carList).
            add("stringSet", stringSet).
            add("carSet", carSet).
            add("stringMap", stringMap).
            add("stringCarMap", stringCarMap);
​
    //Register the above bean to the container
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    factory.registerBeanDefinition("car1", car1);
    factory.registerBeanDefinition("car2", car2);
    factory.registerBeanDefinition("compositeObj", compositeObj);
​
    //Next, we will output all the bean s in the container
    for (String beanName : factory.getBeanDefinitionNames()) {
        System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
    }
}

There are several points to be said:

RuntimeBeanReference: used to represent the bean reference type, similar to ref in xml

ManagedList: if the attribute is of type List, t you need to use this class for operation. This class inherits ArrayList

ManagedSet: if the property is of type Set, t you need to use this class for operation. This class inherits LinkedHashSet

ManagedMap: if the attribute is of Map type, t you need to use this class for operation. This class inherits LinkedHashMap

The above is the result of the combination of these categories.

Take a look at the effect and run the output

car1->Car{name='audi'}
car2->Car{name='Porsche'}
compositeObj->CompositeObj{name='Passerby a Java'
            , salary=50000
            , car1=Car{name='audi'}
            , stringList=[java High concurrency series, mysql series, maven Master Series]
            , carList=[Car{name='audi'}, Car{name='Porsche'}]
            , stringSet=[java High concurrency series, mysql series, maven Master Series]
            , carSet=[Car{name='audi'}, Car{name='Porsche'}]
            , stringMap={Series 1=java High concurrency series, Series 2=Maven Master Series, Series 3=mysql series}
            , stringCarMap={car1=Car{name='audi'}, car2=Car{name='Porsche'}}}

 

Xml file mode

This method has been described many times and we are familiar with it, that is, defining bean s through xml, as follows

<?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">

    <bean id="bean name" class="bean Full class name"/>

</beans>

The bean configuration information in xml will be parsed into BeanDefinition object by the parser, which will be explained in detail in the second stage.

 

properties file

It is estimated that you are unfamiliar with this method. Put the bean definition information in the properties file, and then parse the configuration information into the BeanDefinition object through the parser.

The format of properties is as follows:

employee.(class)=MyClass       // Equivalent to: < bean class = "MyClass" / >
employee.(abstract)=true       // Equivalent to: < bean Abstract = "true" / >
employee.group=Insurance       // Setting a value for a property is equivalent to: < property name = "group" value = "insurance" / >
employee.usesDialUp=false      // Set a value for the usesDialUp property in the employee bean, which is equivalent to: < property name = "usesDialUp" value = "false" / >

salesrep.(parent)=employee     // Define a bean with the ID of salesrep, and specify the parent bean as employee, which is equivalent to: < bean id = "salesrep" parent = "employee" / >
salesrep.(lazy-init)=true      // Setting delayed initialization is equivalent to: < bean lazy init = "true" / >
salesrep.manager(ref)=tony     // Set the manager property value of this bean. It is another bean with the name of tony, which is equivalent to: < property name = "manager" ref = "tony" / >
salesrep.department=Sales      // Equivalent to: < property name = "department" value = "sales" / >

techie.(parent)=employee       // Defines a bean with ID of tech and specifies the parent bean as employee, which is equivalent to: < bean id = "tech" parent = "employee" / >
techie.(scope)=prototype       // Setting the scope of a bean is equivalent to < bean scope = "prototype" / >
techie.manager(ref)=jeff       // Equivalent to: < property name = "manager" ref = "Jeff" / >
techie.department=Engineering  // <property name="department" value="Engineering" />
techie.usesDialUp=true         // <property name="usesDialUp" value="true" />

ceo.$0(ref)=secretary          // Set the first parameter value of the constructor, which is equivalent to: < constructor Arg index = "0" ref = "secret" / >
ceo.$1=1000000                 // Set the second parameter value of the constructor, which is equivalent to: < constructor Arg index = "1" value = "1000000" / >

 

Annotation method

Two common types:

  1. Class is annotated with @ compound annotation to define a bean

  2. The @ bean annotation is used in the configuration class to define beans

 

Summary

Bean registrants only recognize BeanDefinition objects. In any way, they will finally convert the information of these bean definitions into BeanDefinition objects, and then register them in the spring container.

 

Stage 2: Bean meta information parsing stage

The parsing of bean meta information is to parse the bean configuration information defined in various ways into BeanDefinition objects.

 

There are three main ways to parse Bean meta information

  1. Parsing of xml file definition bean

  2. The properties file defines the parsing of the bean

  3. Parsing of annotation defined bean s

 

XML parsing: XmlBeanDefinitionReader

spring provides an XmlBeanDefinitionReader class that parses beans defined in xml into BeanDefinition objects.

Look directly at the case code

A bean xml configuration file

<?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">

    <bean id="car" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="audi"/>
    </bean>

    <bean id="car1" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="Porsche"/>
    </bean>

    <bean id="car2" parent="car1"/>

    <bean id="user" class="com.javacode2018.lesson002.demo1.User">
        <property name="name" value="Passerby a Java"/>
        <property name="car" ref="car1"/>
    </bean>
</beans>

Four bean s are registered above. I won't explain more.

Parse bean xml into BeanDefinition object

/**
 * xml Parsing method bean configuration information
 */
@Test
public void test1() {
    //Define a spring container, which implements BeanDefinitionRegistry by default, so it is itself a bean registrar
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    //To define an xml BeanDefinition reader, you need to pass a BeanDefinitionRegistry (bean registrar) object
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);

    //Specifies the location of the bean xml configuration file
    String location = "classpath:/com/javacode2018/lesson002/demo2/beans.xml";
    //Load the bean xml file through XmlBeanDefinitionReader, and then register the parsed BeanDefinition into the container
    int countBean = xmlBeanDefinitionReader.loadBeanDefinitions(location);
    System.out.println(String.format("Total registered %s individual bean", countBean));

    //Print out the configuration information of the registered bean
    for (String beanName : factory.getBeanDefinitionNames()) {
        //Obtain the corresponding BeanDefinition information from the container by name
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //Which class is used to obtain BeanDefinition
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //Get bean object by name
        Object bean = factory.getBean(beanName);
        //Printout
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName: " + beanDefinitionClassName);
        System.out.println("    beanDefinition: " + beanDefinition);
        System.out.println("    bean: " + bean);
    }
}

The above notes are more detailed, so I won't explain it here.

Note: when creating an XmlBeanDefinitionReader, you need to pass a bean registry, and the BeanDefinition generated in the parsing process will be thrown into the bean registry.

Run output

A total of 4 have been registered bean
car:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean: Car{name='audi'}
car1:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean: Car{name='Porsche'}
car2:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean with parent 'car1': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean: Car{name='Porsche'}
user:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo1.User]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean: User{name='Passerby a Java', car=Car{name='audi'}}

Take a closer look at the above output. These beandefinitions are of the type GenericBeanDefinition, that is, the beans defined in xml are represented by the type GenericBeanDefinition after being parsed.

 

properties file definition bean parsing: PropertiesBeanDefinitionReader

spring provides an XmlBeanDefinitionReader class, which parses beans defined in xml into BeanDefinition objects. The process is similar to that of xml.

Look at the case code.

The bean s defined in the above xml mode are implemented through the properties file.

A properties file: beans properties

car.(class)=com.javacode2018.lesson002.demo1.Car
car.name=audi
​
car1.(class)=com.javacode2018.lesson002.demo1.Car
car1.name=Porsche
​
car2.(parent)=car1
​
user.(class)=com.javacode2018.lesson002.demo1.User
user.name=Passerby a Java
user.car(ref)=car

Parse the bean properties file into a BeanDefinition object

/**
 * properties Parsing file mode bean configuration information
 */
@Test
public void test2() {
    //Define a spring container, which implements BeanDefinitionRegistry by default, so it is itself a bean registrar
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
​
    //To define a BeanDefinition reader for properties, you need to pass a BeanDefinitionRegistry (bean registrar) object
    PropertiesBeanDefinitionReader propertiesBeanDefinitionReader = new PropertiesBeanDefinitionReader(factory);
​
    //Specifies the location of the bean xml configuration file
    String location = "classpath:/com/javacode2018/lesson002/demo2/beans.properties";
    //Load the bean properties file through the PropertiesBeanDefinitionReader, and then register the BeanDefinition generated by parsing into the container
    int countBean = propertiesBeanDefinitionReader.loadBeanDefinitions(location);
    System.out.println(String.format("Total registered %s individual bean", countBean));
​
    //Print out the configuration information of the registered bean
    for (String beanName : factory.getBeanDefinitionNames()) {
        //Obtain the corresponding BeanDefinition information from the container by name
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //Which class is used to obtain BeanDefinition
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //Get bean object by name
        Object bean = factory.getBean(beanName);
        //Printout
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName: " + beanDefinitionClassName);
        System.out.println("    beanDefinition: " + beanDefinition);
        System.out.println("    bean: " + bean);
    }
}

Run output

user:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo1.User]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: User{name='Passerby a Java', car=Car{name='audi'}}
car1:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: Car{name='Porsche'}
car:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: Car{name='audi'}
car2:
    beanDefinitionClassName: org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition: Generic bean with parent 'car1': class [null]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: Car{name='Porsche'}

The output is basically the same as the xml output.

The properties method is not very convenient to use, so we seldom see people using it.

 

Annotation method: PropertiesBeanDefinitionReader

The bean defined by annotation needs to be parsed by using the class PropertiesBeanDefinitionReader. The method is also similar to the above two methods. Let's look at the case directly.

Annotate 2 classes with annotations

Service1

package com.javacode2018.lesson002.demo2;
​
​
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
​
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Primary
@Lazy
public class Service1 {
}

This class uses three annotations. These annotations have been introduced earlier and can be used to configure bean information

The bean above is multi instance.

Service2

package com.javacode2018.lesson002.demo2;

import org.springframework.beans.factory.annotation.Autowired;

public class Service2 {

    @Autowired
    private Service1 service1; //@1

    @Override
    public String toString() {
        return "Service2{" +
                "service1=" + service1 +
                '}';
    }
}

@1: Marked @ Autowired, indicating that this object needs to be injected

The bean defined by the annotation is resolved to BeanDefinition, as follows:

@Test
public void test3() {
    //Define a spring container, which implements BeanDefinitionRegistry by default, so it is itself a bean registrar
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    //To define an annotated BeanDefinition reader, you need to pass a BeanDefinitionRegistry (bean registrar) object
    AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(factory);

    //Load the bean properties file through the PropertiesBeanDefinitionReader, and then register the BeanDefinition generated by parsing into the container
    annotatedBeanDefinitionReader.register(Service1.class, Service2.class);

    //Print out the configuration information of the registered bean
    for (String beanName : new String[]{"service1", "service2"}) {
        //Obtain the corresponding BeanDefinition information from the container by name
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //Which class is used to obtain BeanDefinition
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //Get bean object by name
        Object bean = factory.getBean(beanName);
        //Printout
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName: " + beanDefinitionClassName);
        System.out.println("    beanDefinition: " + beanDefinition);
        System.out.println("    bean: " + bean);
    }
}

Run output

service1:
    beanDefinitionClassName: org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo2.Service1]; scope=prototype; abstract=false; lazyInit=true; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: com.javacode2018.lesson002.demo2.Service1@21a947fe
service2:
    beanDefinitionClassName: org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo2.Service2]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: Service2{service1=null}

It can be seen from the output that lazyInit in the bean definition of service1 is true, the primary is true, and the scope is prototype. It indicates that three annotation information are marked on the annotation of class service1, and the information is parsed and placed in the bean definition.

Note: why is service1 null in the last line? Isn't it marked @ Autowired?

This place is spoiled in advance. It doesn't matter if you don't understand it. You'll understand it after this article is over.

Adjust the above code and add the following @1 line of code as follows:

@Test
public void test3() {
    //Define a spring container, which implements BeanDefinitionRegistry by default, so it is itself a bean registrar
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
​
    //To define an annotated BeanDefinition reader, you need to pass a BeanDefinitionRegistry (bean registrar) object
    AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(factory);
​
    //Load the bean properties file through the PropertiesBeanDefinitionReader, and then register the BeanDefinition generated by parsing into the container
    annotatedBeanDefinitionReader.register(Service1.class, Service2.class);
​
    factory.getBeansOfType(BeanPostProcessor.class).values().forEach(factory::addBeanPostProcessor); // @1
    //Print out the configuration information of the registered bean
    for (String beanName : new String[]{"service1", "service2"}) {
        //Obtain the corresponding BeanDefinition information from the container by name
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //Which class is used to obtain BeanDefinition
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //Get bean object by name
        Object bean = factory.getBean(beanName);
        //Printout
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName: " + beanDefinitionClassName);
        System.out.println("    beanDefinition: " + beanDefinition);
        System.out.println("    bean: " + bean);
    }
}

Run again, and the last line has a value:

service1:
    beanDefinitionClassName: org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo2.Service1]; scope=prototype; abstract=false; lazyInit=true; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: com.javacode2018.lesson002.demo2.Service1@564718df
service2:
    beanDefinitionClassName: org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo2.Service2]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean: Service2{service1=com.javacode2018.lesson002.demo2.Service1@52aa2946}

At present, we have reached the second stage, and there are still 14 stages. The content of this article is relatively long. It is suggested to collect it first and look at it slowly. Let's continue.

 

Stage 3: Spring Bean registration stage

The bean registration phase requires a very important interface: BeanDefinitionRegistry

 

Bean registration interface: BeanDefinitionRegistry

This interface defines some methods commonly used to register bean s. The source code is as follows:

public interface BeanDefinitionRegistry extends AliasRegistry {

    /**
     * Register a new bean definition
     * beanName: bean Name of
     * beanDefinition: bean Definition information
     */
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;

    /**
     * Remove registered beans by bean name
     * beanName: bean name
     */
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    /**
     * Get bean definition information by name
     * beanName: bean name
     */
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    /**
     * Check whether beanName has been registered
     */
    boolean containsBeanDefinition(String beanName);

    /**
     * Get the list of bean names that have been defined (registered)
     */
    String[] getBeanDefinitionNames();

    /**
     * Returns the number of registered bean s in the Registrar
     */
    int getBeanDefinitionCount();

    /**
     * Determines whether the given bean name or alias is already used in this registry
     * beanName: It can be a bean name or an alias of a bean
     */
    boolean isBeanNameInUse(String beanName);

}

 

Alias registration interface: alias registry

The BeanDefinitionRegistry interface inherits the alias registry interface, which defines some methods for operating bean aliases. Take a look at its source code:

public interface AliasRegistry {

    /**
     * Assign alias to name
     */
    void registerAlias(String name, String alias);

    /**
     * Removes the specified alias from this registry
     */
    void removeAlias(String alias);

    /**
     * Determine whether name has been used as an alias
     */
    boolean isAlias(String name);

    /**
     * Returns all aliases corresponding to name
     */
    String[] getAliases(String name);

}

 

The only implementation of BeanDefinitionRegistry: DefaultListableBeanFactory

The BeanDefinitionRegistry interface in spring has a unique implementation class:

org.springframework.beans.factory.support.DefaultListableBeanFactory

You may see that many classes also implement the BeanDefinitionRegistry interface, such as the AnnotationConfigApplicationContext we often use, but in fact, it is forwarded internally to DefaultListableBeanFactory for processing, so the class that really implements this interface is DefaultListableBeanFactory.

Let's look back at the first few cases. DefaultListableBeanFactory is used as the bean registrar. At this time, you should be able to understand why.

Let's take a case to demonstrate some of the methods commonly used above.

 

case

code

package com.javacode2018.lesson002.demo3;

import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;

import java.util.Arrays;

/**
 * BeanDefinitionRegistry case
 */
public class BeanDefinitionRegistryTest {

    @Test
    public void test1() {
        //Create a bean factory, which implements the BeanDefinitionRegistry interface by default, so it is also a bean registrar
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //Define a bean
        GenericBeanDefinition nameBdf = new GenericBeanDefinition();
        nameBdf.setBeanClass(String.class);
        nameBdf.getConstructorArgumentValues().addIndexedArgumentValue(0, "Passerby a Java");

        //Register bean s into containers
        factory.registerBeanDefinition("name", nameBdf);

        //Get BeanDefinition by name
        System.out.println(factory.getBeanDefinition("name"));
        //Judge whether BeanDefinition has been registered by name
        System.out.println(factory.containsBeanDefinition("name"));
        //Get the names of all registrations
        System.out.println(Arrays.asList(factory.getBeanDefinitionNames()));
        //Gets the number of registered beandefinitions
        System.out.println(factory.getBeanDefinitionCount());
        //Judge whether the specified name has been used
        System.out.println(factory.isBeanNameInUse("name"));

        //Alias related methods
        //Register 2 aliases for name
        factory.registerAlias("name", "alias-name-1");
        factory.registerAlias("name", "alias-name-2");

        //Determine whether alias-name-1 has been used as an alias
        System.out.println(factory.isAlias("alias-name-1"));

        //Get all corresponding aliases by name
        System.out.println(Arrays.asList(factory.getAliases("name")));

        //Finally, let's get the bean
        System.out.println(factory.getBean("name"));


    }
}

Run output

Generic bean: class [java.lang.String]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
true
[name]
1
true
true
[alias-name-2, alias-name-1]
Passerby a Java

The operations to be introduced from stage 4 to stage 14, that is, from the BeanDefinition merging stage to the bean initialization completion stage, are all sent during the process of calling getBean to obtain the bean object from the container. Please pay attention to the details. If you go on, it is recommended to look at the source code of getBean. The following processes come from this method:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

 

Phase 4: BeanDefinition merge phase

 

What does the consolidation phase do?

Maybe there is a parent-child bean relationship when defining beans. At this time, the information in the child BeanDefinition is incomplete. For example, when setting properties, it is configured in the parent BeanDefinition. At this time, there is no such information in the child BeanDefinition. It is necessary to merge the child bean's BeanDefinition with the parent bean's BeanDefinition to obtain the final RootBeanDefinition, The RootBeanDefinition obtained after merging contains all the information of the bean definition and all the information inherited from the parent bean relay. All subsequent bean creation work depends on the merged BeanDefinition.

Merging BeanDefinition will use the following method:

org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition

bean definitions may have multi-level parent-child relationships. When merging, recursive merging is carried out to finally get a RootBeanDefinition containing complete information

 

case

Come to an ordinary class

package com.javacode2018.lesson002.demo4;

public class LessonModel {
    //Course name
    private String name;
    //class hour
    private int lessonCount;
    //Description information
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLessonCount() {
        return lessonCount;
    }

    public void setLessonCount(int lessonCount) {
        this.lessonCount = lessonCount;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "LessonModel{" +
                "name='" + name + '\'' +
                ", lessonCount=" + lessonCount +
                ", description='" + description + '\'' +
                '}';
    }
}

Define three bean s with parent-child relationship through xml

<?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">

    <bean id="lesson1" class="com.javacode2018.lesson002.demo4.LessonModel"/>

    <bean id="lesson2" parent="lesson1">
        <property name="name" value="spring Master Series"/>
        <property name="lessonCount" value="100"/>
    </bean>

    <bean id="lesson3" parent="lesson2">
        <property name="description" value="Passerby a Java Take you to learn spring,Over 90%developer!"/>
    </bean>

</beans>

lesson2 is the son of lesson1, and lesson3 is the grandson of lesson1.

Parsing xml registered bean s

Next, we will parse the xml, register the bean, and then traverse the name of the output bean, the original BeanDefinition registered in the parsing process, the merged BeanDefinition, and the attribute information in the BeanDefinition before and after the merging

package com.javacode2018.lesson002.demo4;

import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

/**
 * BeanDefinition merge
 */
public class MergedBeanDefinitionTest {
    @Test
    public void test1() {
        //Create bean container
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //Create a bean xml parser
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(factory);
        //Parse the bean xml and register the BeanDefinition generated in the parsing process into the DefaultListableBeanFactory
        beanDefinitionReader.loadBeanDefinitions("com/javacode2018/lesson002/demo4/beans.xml");
        //Traverse all bean information registered in the container
        for (String beanName : factory.getBeanDefinitionNames()) {
            //Obtain the original registered BeanDefinition information through the bean name
            BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
            //Get the merged BeanDefinition information
            BeanDefinition mergedBeanDefinition = factory.getMergedBeanDefinition(beanName);

            System.out.println(beanName);
            System.out.println("analysis xml Registered in the process beanDefinition: " + beanDefinition);
            System.out.println("beanDefinition Attribute information in" + beanDefinition.getPropertyValues());
            System.out.println("After the merger mergedBeanDefinition: " + mergedBeanDefinition);
            System.out.println("mergedBeanDefinition Attribute information in" + mergedBeanDefinition.getPropertyValues());
            System.out.println("---------------------------");
        }
    }
}

Run output

lesson1
 analysis xml Registered in the process beanDefinition: Generic bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
beanDefinition Attribute information in PropertyValues: length=0
 After the merger mergedBeanDefinition: Root bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
mergedBeanDefinition Attribute information in PropertyValues: length=0
---------------------------
lesson2
 analysis xml Registered in the process beanDefinition: Generic bean with parent 'lesson1': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
beanDefinition Attribute information in PropertyValues: length=2; bean property 'name'; bean property 'lessonCount'
After the merger mergedBeanDefinition: Root bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
mergedBeanDefinition Attribute information in PropertyValues: length=2; bean property 'name'; bean property 'lessonCount'
---------------------------
lesson3
 analysis xml Registered in the process beanDefinition: Generic bean with parent 'lesson2': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
beanDefinition Attribute information in PropertyValues: length=1; bean property 'description'
After the merger mergedBeanDefinition: Root bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
mergedBeanDefinition Attribute information in PropertyValues: length=3; bean property 'name'; bean property 'lessonCount'; bean property 'description'
---------------------------

From the output results, we can see that before merging, the BeanDefinition is incomplete, the class in lesson2 and lesson3 is null, and the attribute information is incomplete, but these information are complete after merging.

Before merging, it is of GenericBeanDefinition type, and after merging, it is of RootBeanDefinition type.

When obtaining the BeanDefinition merged by lesson3, it will merge recursively internally. First merge lesson1 and lesson2, then merge lesson2 and lesson3, and finally get the merged BeanDefinition.

Later stages will use the RootBeanDefinition generated by the merge.

 

Phase 5: Bean Class loading phase

This stage is to convert the class name of the bean into an object of class type.

There is a field of Object type in BeanDefinition: beanClass

private volatile Object beanClass;

It is used to represent the class object of the bean. Generally, the value of this field has two types: one is the object of the class type corresponding to the bean, and the other is the complete class name of the class corresponding to the bean. In the first case, it does not need to be resolved. In the second case, when this field is the class name of the bean, it needs to be converted into a class object through the class loader.

At this time, the beanClass in the RootBeanDefinition merged in phase 4 will be resolved, the Class name of the bean will be converted into a Class object, and then assigned to the beanClass field.

Source location:

org.springframework.beans.factory.support.AbstractBeanFactory#resolveBeanClass

After instantiating the beanclass object, you will get the beanclass object in the upper stage and the beanclass object in the lower stage.

Bean instantiation is divided into three stages: pre stage, instantiation stage and post stage; The following is a detailed introduction.

 

Phase 6: Bean instantiation phase

 

It is divided into two small stages

  1. Operation before Bean instantiation

  2. Bean instantiation operation

 

Operation before Bean instantiation

Let's take a look at DefaultListableBeanFactory. There is a very important field in this class:

private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

Is a collection of BeanPostProcessor types

BeanPostProcessor is an interface with many sub interfaces. Many methods are provided in these interfaces. spring will call some methods in the BeanPostProcessor in the above list at different stages of the bean life cycle to extend the life cycle. All extension points in the bean life cycle depend on the BeanPostProcessor in this collection, Therefore, if you want to intervene in the life cycle of beans, you must master this.

Note: in this article, many that end with BeanPostProcessor implement the BeanPostProcessor interface, some of which are implemented directly, and some of which implement its sub interfaces.

A piece of code will be called before Bean instantiation:

@Nullable
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

This code leaves a gap for developers before bean instantiation. Developers can directly create an object as a bean instance in this place and skip the process of instantiating beans within spring.

In the above code, the list of bean postprocessors is polled. If the type is InstantiationAwareBeanPostProcessor, try to call InstantiationAwareBeanPostProcessor #postprocessbeforeinstance to obtain the instance object of the bean. If it can be obtained, the return value will be used as the instance of the current bean, and the process of instantiating the bean provided by spring will be skipped.

The postprocessbeforeinstance method is as follows:

default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    return null;
}

This place provides developers with an extension point that allows developers to directly return an instance of the bean in this method.

Let's take a case.

case

package com.javacode2018.lesson002.demo5;

import com.javacode2018.lesson002.demo1.Car;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.lang.Nullable;

/**
 * bean In the pre initialization stage, it will call: {@ link org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInitialization(Object, String)}
 */
public class InstantiationAwareBeanPostProcessorTest {
    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //Add a BeanPostProcessor: InstantiationAwareBeanPostProcessor
        factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { //@1
            @Nullable
            @Override
            public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
                System.out.println("call postProcessBeforeInstantiation()");
                //When it is found that the type is Car type, hard code to create a Car object to return
                if (beanClass == Car.class) {
                    Car car = new Car();
                    car.setName("Porsche");
                    return car;
                }
                return null;
            }
        });

        //Define a car bean. The car name is Audi
        AbstractBeanDefinition carBeanDefinition = BeanDefinitionBuilder.
                genericBeanDefinition(Car.class).
                addPropertyValue("name", "audi").  //@2
                getBeanDefinition();
        factory.registerBeanDefinition("car", carBeanDefinition);
        //Get the instance of car bean from the container and output it
        System.out.println(factory.getBean("car"));

    }
}

@1: An InstantiationAwareBeanPostProcessor was created and dropped into the BeanPostProcessor list in the container

@2: Create a car bean with the name Audi

Run output

call postProcessBeforeInstantiation()
Car{name='Porsche'}

When the bean is defined, the name is Audi, and the final output is Porsche

The reason why the definition and output are inconsistent is that we manually created an instance in the instantiawarebeanpostprocessor #postprocessbeforeinstance method and returned it directly instead of relying on spring to create this instance.

Summary

In fact, very little is used to intervene in the creation of beans in the pre instantiation stage, so most bean creation will continue to go through the following stages.

 

Bean instantiation operation

What can this process do?

This process will call the bean constructor through reflection to create an instance of the bean.

spring provides an interface for developers to determine which constructor to use.

Take a look at the logic of this Code:

for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
        Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
        if (ctors != null) {
            return ctors;
        }
    }
}

The determineCandidateConstructors method of the smartinstantiaawarebeanpostprocessor interface will be called. This method will return the list of candidate constructors or empty. Take a look at the source code of this method:

@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {

    return null;
}

This method has an important implementation class

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

The method marked with @ Autowired can be returned as a candidate constructor. Those interested can take a look at the code.

case

Let's take a case to customize an annotation. When the constructor is annotated by this annotation, spring will automatically choose to use this constructor to create objects.

Customize an annotation

The following annotation can be marked on the constructor. After using this annotation, the constructor will be used when creating bean s.

package com.javacode2018.lesson002.demo6;
​
import java.lang.annotation.*;
​
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowried {
}

Let's have an ordinary class

The following class has three constructors, one of which uses @ myautowritten as the method of bean instantiation.

package com.javacode2018.lesson002.demo6;
​
public class Person {
    private String name;
    private Integer age;
​
    public Person() {
        System.out.println("call Person()");
    }
​
    @MyAutowried
    public Person(String name) {
        System.out.println("call Person(String name)");
        this.name = name;
    }
​
    public Person(String name, Integer age) {
        System.out.println("call Person(String name, int age)");
        this.name = name;
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Customize a SmartInstantiationAwareBeanPostProcessor

Logic of code: return the constructor list marked with @ myautowritten

package com.javacode2018.lesson002.demo6;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.lang.Nullable;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
    @Nullable
    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println(beanClass);
        System.out.println("call MySmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors method");
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        if (declaredConstructors != null) {
            //Get the list of constructors with @ myautowritten annotation
            List<Constructor<?>> collect = Arrays.stream(declaredConstructors).
                    filter(constructor -> constructor.isAnnotationPresent(MyAutowried.class)).
                    collect(Collectors.toList());
            Constructor[] constructors = collect.toArray(new Constructor[collect.size()]);
            return constructors.length != 0 ? constructors : null;
        } else {
            return null;
        }
    }
}

Let's have a test case

package com.javacode2018.lesson002.demo6;

import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * Use {@ link org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors(Class, String)} to determine which constructor to use to create bean instances
 */
public class SmartInstantiationAwareBeanPostProcessorTest {
    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //Create a SmartInstantiationAwareBeanPostProcessor and add it to the container
        factory.addBeanPostProcessor(new MySmartInstantiationAwareBeanPostProcessor());

        factory.registerBeanDefinition("name",
                BeanDefinitionBuilder.
                        genericBeanDefinition(String.class).
                        addConstructorArgValue("Passerby a Java").
                        getBeanDefinition());

        factory.registerBeanDefinition("age",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Integer.class).
                        addConstructorArgValue(30).
                        getBeanDefinition());

        factory.registerBeanDefinition("person",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Person.class).
                        getBeanDefinition());

        Person person = factory.getBean("person", Person.class);
        System.out.println(person);

    }
}

Run output

class com.javacode2018.lesson002.demo6.Person
 call MySmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors method
class java.lang.String
 call MySmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors method
 call Person(String name)
Person{name='Passerby a Java', age=null}

It can be seen from the output that the constructor marked @ MyAutowired in Person is called.

So far, the bean instantiation phase is over, and continue to the later phase.

 

Stage 7: BeanDefinition processing after merging

The source code of this piece is as follows

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof MergedBeanDefinitionPostProcessor) {
                MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
                bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
            }
        }
    }

The postProcessMergedBeanDefinition method of the mergedbeandefinition postprocessor interface will be called. Take a look at the source code of this method:

void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

spring will poll BeanPostProcessor and call MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition in turn

The first parameter is beandefinition, which represents the merged RootBeanDefinition. We can process the merged beandefinition again inside this method

postProcessMergedBeanDefinition has two implementation classes, which we introduced earlier. It is also widely used. You will often ask during the interview:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
 stay postProcessMergedBeanDefinition In the method @Autowired,@Value Cache the marked methods and fields

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
 stay postProcessMergedBeanDefinition In the method @Resource Labeled fields@Resource Annotation method @PostConstruct Labeled fields @PreDestroy Annotation method for caching

 

Stage 8: Bean property setting stage

 

The property setting stage is divided into three small stages

  • Post instantiation phase

  • Pre processing of Bean attribute assignment

  • Bean attribute assignment

 

Post instantiation phase

The postprocessafterinstance method of the instantiaawarebeanpostprocessor interface will be called. The calling logic is as follows:

The specific calling logic is as follows:

for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
            return;
        }
    }
}

When the postprocessafterinstance method returns false, the subsequent pre-processing of Bean attribute assignment and Bean attribute assignment will be skipped.

Let's take a look at the definition of the postprocessafterinstance method

default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    return true;
}

In the case of skipping the assignment, we return false.

case

Let's have a class

package com.javacode2018.lesson002.demo7;


public class UserModel {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "UserModel{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

test case

The following is very simple to register a bean of UserModel

package com.javacode2018.lesson002.demo7;
​
​
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
​
/**
 * {@link InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation(java.lang.Object, java.lang.String)}
 * Return false to prevent the assignment of bean properties
 */
public class InstantiationAwareBeanPostProcessoryTest1 {
​
    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
​
        factory.registerBeanDefinition("user1", BeanDefinitionBuilder.
                genericBeanDefinition(UserModel.class).
                addPropertyValue("name", "Passerby a Java").
                addPropertyValue("age", 30).
                getBeanDefinition());
​
        factory.registerBeanDefinition("user2", BeanDefinitionBuilder.
                genericBeanDefinition(UserModel.class).
                addPropertyValue("name", "Lau Andy").
                addPropertyValue("age", 50).
                getBeanDefinition());
​
        for (String beanName : factory.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
        }
    }
​
}

The above defines two bean s: [user1,user2], which are output after obtaining

Run output

user1->UserModel{name='Passerby a Java', age=30}
user2->UserModel{name='Lau Andy', age=50}

At this time, both properties in UserModel have values.

Let's stop the assignment of user1, modify the code and add the following code:

factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("user1".equals(beanName)) {
            return false;
        } else {
            return true;
        }
    }
});

Run the test output again:

user1->UserModel{name='null', age=null}
user2->UserModel{name='Lau Andy', age=50}

The attribute assignment of user1 was skipped.

 

Stage before Bean attribute assignment

In this stage, the postProcessProperties method of the instantiawarebeanpostprocessor interface will be called. The calling logic is as follows:

for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
            if (filteredPds == null) {
                filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
            pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                return;
            }
        }
        pvs = pvsToUse;
    }
}

As can be seen from the above, if the postProcessProperties and postProcessPropertyValues in the instantiawarebean postprocessor return to null, it means that the bean does not need to set properties and returns directly to the next stage.

Let's take a look at the definition of the postProcessProperties method:

@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
    throws BeansException {

    return null;
}

PropertyValues saves the settings of all property values in the bean instance object, so we can modify the PropertyValues value in this method.

This method has two important implementation classes

AutowiredAnnotationBeanPostProcessor injects values into the fields and methods marked with @ Autowired and @ Value in this method.

CommonAnnotationBeanPostProcessor injects values into the fields and methods marked with @ Resource in this method.

Let's take a case in which we modify pvs.

case

Case code

@Test
public void test3() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { // @0
        @Nullable
        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            if ("user1".equals(beanName)) {
                if (pvs == null) {
                    pvs = new MutablePropertyValues();
                }
                if (pvs instanceof MutablePropertyValues) {
                    MutablePropertyValues mpvs = (MutablePropertyValues) pvs;
                    //Set name to: passerby
                    mpvs.add("name", "Passerby");
                    //Change the value of the age attribute to 18
                    mpvs.add("age", 18);
                }
            }
            return null;
        }
    });

    //Note that user1 does not set a value for this property
    factory.registerBeanDefinition("user1", BeanDefinitionBuilder.
            genericBeanDefinition(UserModel.class).
            getBeanDefinition()); //@1

    factory.registerBeanDefinition("user2", BeanDefinitionBuilder.
            genericBeanDefinition(UserModel.class).
            addPropertyValue("name", "Lau Andy").
            addPropertyValue("age", 50).
            getBeanDefinition());

    for (String beanName : factory.getBeanDefinitionNames()) {
        System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
    }
}

@1: user1 bean does not set the value of the property

@0: this implementation org springframework. beans. factory. config. The instantiawarebeanpostprocessor #postprocessproperties method modifies the property value information of the bean user1 internally.

Run output

user1->UserModel{name='Passerby', age=18}
user2->UserModel{name='Lau Andy', age=50}

All the above processes are ok. Enter the bean assignment operation

 

Bean attribute assignment stage

This process is relatively simple. Loop the property value information in PropertyValues, and set the property value to the bean instance by calling the set method through reflection.

The value in PropertyValues is configured through the property element in bean xml, or the value set by calling the add method in MutablePropertyValues.

 

Phase 9: Bean initialization phase

 

This stage is divided into five small stages

  • Bean Aware interface callback

  • Operation before Bean initialization

  • Bean initialization operation

  • Operation after Bean initialization

  • Bean initialization completed

 

Bean Aware interface callback

Source code of this piece:

private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

If our bean instance implements the above interface, it will be called in the following order:

BeanNameAware: take bean Inject your name into it
BeanClassLoaderAware: take BeanClassLoader Inject in
BeanFactoryAware: take BeanFactory Inject in

Take a case and feel it

A class to implement the above three interfaces.

package com.javacode2018.lesson002.demo8;
​
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
​
public class AwareBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware {
​
    @Override
    public void setBeanName(String name) {
        System.out.println("setBeanName: " + name);
    }
​
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("setBeanFactory: " + beanFactory);
    }
​
    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("setBeanClassLoader: " + classLoader);
    }
​
}

Take a test class and create the bean of the above object

package com.javacode2018.lesson002.demo8;
​
import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
​
public class InvokeAwareTest {
​
    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.registerBeanDefinition("awareBean", BeanDefinitionBuilder.genericBeanDefinition(AwareBean.class).getBeanDefinition());
        //Calling the getBean method to get the bean will trigger the initialization of the bean
        factory.getBean("awareBean");
    }
}

Run output

setBeanName: awareBean
setBeanClassLoader: sun.misc.Launcher$AppClassLoader@18b4aac2
setBeanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory@5bb21b69: defining beans [awareBean]; root of factory hierarchy

 

Operation before Bean initialization

Source code of this stage:

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

The postProcessBeforeInitialization method of BeanPostProcessor will be called. If null is returned, the current method will end.

It is usually called postProcessBeforeInitialization. This method is: operation before bean initialization.

This interface has two implementation classes, which are more important:

org.springframework.context.support.ApplicationContextAwareProcessor
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

ApplicationContextAwareProcessor injects six Aware interface objects

If the bean implements the following interface, the methods in the following interface will be called in turn in ApplicationContextAwareProcessor#postProcessBeforeInitialization to inject the object corresponding to the Aware prefix into the bean instance.

EnvironmentAware: injection Environment object
EmbeddedValueResolverAware: injection EmbeddedValueResolver object
ResourceLoaderAware: injection ResourceLoader object
ApplicationEventPublisherAware: injection ApplicationEventPublisher object
MessageSourceAware: injection MessageSource object
ApplicationContextAware: injection ApplicationContext object

From the name, we can see that this class starts with ApplicationContext, indicating that this class can only be used in the ApplicationContext environment.

CommonAnnotationBeanPostProcessor calls the method of @ PostConstruct annotation

The CommonAnnotationBeanPostProcessor#postProcessBeforeInitialization will call all methods annotated with @ PostConstruct annotation in the bean

Take a case and feel it.

case

Let's have a class

The following class has two methods marked with @ PostConstruct and implements the six Aware interfaces mentioned above.

package com.javacode2018.lesson002.demo9;

import org.springframework.beans.BeansException;
import org.springframework.context.*;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringValueResolver;

import javax.annotation.PostConstruct;

public class Bean1 implements EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware {

    @PostConstruct
    public void postConstruct1() { //@1
        System.out.println("postConstruct1()");
    }

    @PostConstruct
    public void postConstruct2() { //@2
        System.out.println("postConstruct2()");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("setApplicationContext:" + applicationContext);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println("setApplicationEventPublisher:" + applicationEventPublisher);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        System.out.println("setEmbeddedValueResolver:" + resolver);
    }

    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("setEnvironment:" + environment.getClass());
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        System.out.println("setMessageSource:" + messageSource);
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        System.out.println("setResourceLoader:" + resourceLoader);
    }
}

Let's have a test case

package com.javacode2018.lesson002.demo9;


import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class PostProcessBeforeInitializationTest {

    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Bean1.class);
        context.refresh();
    }
}

Run output

setEmbeddedValueResolver:org.springframework.beans.factory.config.EmbeddedValueResolver@15b204a1
setResourceLoader:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
setApplicationEventPublisher:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
setMessageSource:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
setApplicationContext:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
postConstruct1()
postConstruct2()

You can take a look at the source code of AnnotationConfigApplicationContext. Many beanpostprocessors will be added to the DefaultListableBeanFactory.

 

Bean initialization phase

2 steps

  1. Call the afterpropertieset method of the InitializingBean interface

  2. Call the initialization method specified when defining the bean.

Call the afterpropertieset method of the InitializingBean interface

Take a look at the InitializingBean interface

public interface InitializingBean {
​
    void afterPropertiesSet() throws Exception;
​
}

When our bean implements this interface, it will be called at this stage

The initialization method specified when calling the bean definition

Let's take a look at how to specify the initialization method of bean s. There are three ways

Mode 1: specify the initialization method in xml mode

<bean init-method="bean Method name in"/>

Method 2: @ Bean specifies the initialization method

@Bean(initMethod = "Initialization method")

Method 3: specify the initialization method in the way of api

this.beanDefinition.setInitMethodName(methodName);

The initialization method will eventually be assigned to the following field

org.springframework.beans.factory.support.AbstractBeanDefinition#initMethodName

case

Let's have a class

package com.javacode2018.lesson002.demo10;

import org.springframework.beans.factory.InitializingBean;

public class Service implements InitializingBean{
    public void init() {
        System.out.println("call init()method");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("call afterPropertiesSet()");
    }
}

Next, we define the Service bean and specify the init method as the initialization method

package com.javacode2018.lesson002.demo10;

import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * Initialization method test
 */
public class InitMethodTest {

    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        BeanDefinition service = BeanDefinitionBuilder.genericBeanDefinition(Service.class).
                setInitMethodName("init"). //@1: Specify initialization method
                getBeanDefinition();

        factory.registerBeanDefinition("service", service);

        System.out.println(factory.getBean("service"));
    }
}

Run output

call afterPropertiesSet()
call init()method
com.javacode2018.lesson002.demo10.Service@12f41634

Call sequence: after propertieset in InitializingBean, and then call the custom initialization method

 

Post Bean initialization phase

Source code of this piece:

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

Call the postProcessAfterInitialization method of the BeanPostProcessor interface. When null is returned, the above operation will be interrupted.

It is usually called postProcessAfterInitialization. This method is: bean initialization post operation.

Let's take a case:

package com.javacode2018.lesson002.demo11;

import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.lang.Nullable;

/**
 * {@link BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)}
 * bean Post initialization processing
 */
public class PostProcessAfterInitializationTest {

    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //Implementation of adding bean initialization post processor method
        factory.addBeanPostProcessor(new BeanPostProcessor() {
            @Nullable
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                System.out.println("postProcessAfterInitialization: " + beanName);
                return bean;
            }
        });

        //Next, register two String type bean s
        factory.registerBeanDefinition("name",
                BeanDefinitionBuilder.
                        genericBeanDefinition(String.class).
                        addConstructorArgValue("The official account: passerby Java]").
                        getBeanDefinition());
        factory.registerBeanDefinition("personInformation",
                BeanDefinitionBuilder.genericBeanDefinition(String.class).
                        addConstructorArgValue("Lead you to become java killer!").
                        getBeanDefinition());

        System.out.println("-------output bean information---------");

        for (String beanName : factory.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
        }
    }
}

Run output

-------output bean information---------
postProcessAfterInitialization: name
name->The official account: passerby Java]
postProcessAfterInitialization: personInformation
personInformation->Lead you to become java killer!

 

Stage 10: the stage after initialization of all singleton bean s is completed

After all singleton bean s are instantiated, spring will call back the following interface:

public interface SmartInitializingSingleton {
    void afterSingletonsInstantiated();
}

The call logic is in the following method

/**
 * Ensure that all non lazy singletons are instantiated, taking into account FactoryBeans. If necessary, it is usually called at the end of factory setting.
 */
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

This method will trigger the initialization of all non delayed loaded singleton beans internally, and then find the beans of type smartinitializingsingsingleton from the container and call their afterSingletonsInstantiated method.

If you are interested, you can take a look at the container with ApplicationContext. The above method will eventually be called internally to trigger the initialization of all singleton bean s.

Take two cases to demonstrate the use of smartinitializingsingsingleton.

 

Case 1: ApplicationContext automatically callback smartinitializingsingsingleton interface

Service1:

package com.javacode2018.lesson002.demo12;
​
import org.springframework.stereotype.Component;
​
@Component
public class Service1 {
​
    public Service1() {
        System.out.println("create " + this.getClass());
    }
}

Service2:

package com.javacode2018.lesson002.demo12;
​
import org.springframework.stereotype.Component;
​
@Component
public class Service2 {
    public Service2() {
        System.out.println("create " + this.getClass());
    }
}

Customize a smartinitializingsingsingleton

package com.javacode2018.lesson002.demo12;
​
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Component;
​
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("All bean Initialization complete!");
    }
}

To a test class, register the above three bean s through package scanning

package com.javacode2018.lesson002.demo12;
​
import org.junit.Test;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
​
/**
 * After all bean s are initialized, the container will call back {@ link smartinitializingsingsingleton #aftersingletonsinstantiated()}
 */
@ComponentScan
public class SmartInitializingSingletonTest {
    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(SmartInitializingSingletonTest.class);
        System.out.println("Start container!");
        context.refresh();
        System.out.println("Container startup completed!");
    }
}

Run output

Start container!
create class com.javacode2018.lesson002.demo12.Service1
create class com.javacode2018.lesson002.demo12.Service2
 All bean Initialization complete!
Container startup completed!

 

Case 2: let DefaultListableBeanFactory call back smartinitializingsingsingleton through api

@Test
public void test2() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    factory.registerBeanDefinition("service1", BeanDefinitionBuilder.genericBeanDefinition(Service1.class).getBeanDefinition());
    factory.registerBeanDefinition("service2", BeanDefinitionBuilder.genericBeanDefinition(Service2.class).getBeanDefinition());
    factory.registerBeanDefinition("mySmartInitializingSingleton", BeanDefinitionBuilder.genericBeanDefinition(MySmartInitializingSingleton.class).getBeanDefinition());
    System.out.println("Prepare to trigger all singletons bean initialization");
    //Trigger the initialization of all bean s and call back the smartinitializingsingsingleton #aftersingletonsinstantiated method
    factory.preInstantiateSingletons();
}

The bean is registered through the api above

Finally, call factory.. Preinstantiatesingletons triggers the initialization of all non lazy singleton beans. After all beans are assembled, the smartinitializingsingsingleton interface will be called back.

 

Phase 11: Bean usage phase

Not at this stage. After calling the getBean method to get the bean, you can use it and play it at will.

 

Stage 12: Bean destruction stage

 

Several ways to trigger bean destruction

  1. Call org springframework. beans. factory. support. AbstractAutowireCapableBeanFactory#destroyBean

  2. Call org springframework. beans. factory. config. ConfigurableBeanFactory#destroySingletons

  3. Call the close method in ApplicationContext

 

The Bean destruction phase will be executed in turn

  1. Poll the bean postprocessors list. If it is a destructionawarebeanepostprocessor of this type, it will call its internal postProcessBeforeDestruction method

  2. If the bean implements org springframework. beans. factory. The disposablebean interface will call the destroy method in this interface

  3. Call bean custom destroy method

 

DestructionAwareBeanPostProcessor interface

Take a look at the source code:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
​
    /**
     * bean Method of calling before destruction
     */
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
​
    /**
     * It is used to determine whether the bean needs to trigger the postprocessbeforeconstruction method
     */
    default boolean requiresDestruction(Object bean) {
        return true;
    }
​
}

This interface has a key implementation class:

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

The CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction method will call all methods marked with @ PreDestroy in the bean.

 

Let's talk about three ways to customize the destruction method

Method 1: specify the destruction method in xml

<bean destroy-method="bean Method name in"/>

Method 2: specify the destruction method in @ Bean

@Bean(destroyMethod = "Initialization method")

Method 3: specify the destruction method by api

this.beanDefinition.setDestroyMethodName(methodName);

The initialization method will eventually be assigned to the following field

org.springframework.beans.factory.support.AbstractBeanDefinition#destroyMethodName

Let's look at the case of destruction

 

Case 1: Custom DestructionAwareBeanPostProcessor

Let's have a class

package com.javacode2018.lesson002.demo13;
​
public class ServiceA {
    public ServiceA() {
        System.out.println("create " + this.getClass());
    }
}

Customize a DestructionAwareBeanPostProcessor

package com.javacode2018.lesson002.demo13;
​
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
​
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {
    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        System.out.println("Ready to destroy bean: " + beanName);
    }
}

Let's have a test class

package com.javacode2018.lesson002.demo13;
​
import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
​
/**
 * Customize {@ link org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor}
 */
public class DestructionAwareBeanPostProcessorTest {
​
    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
​
        //Add a custom DestructionAwareBeanPostProcessor
        factory.addBeanPostProcessor(new MyDestructionAwareBeanPostProcessor());
​
        //Inject three singleton bean s into the container
        factory.registerBeanDefinition("serviceA1", BeanDefinitionBuilder.genericBeanDefinition(ServiceA.class).getBeanDefinition());
        factory.registerBeanDefinition("serviceA2", BeanDefinitionBuilder.genericBeanDefinition(ServiceA.class).getBeanDefinition());
        factory.registerBeanDefinition("serviceA3", BeanDefinitionBuilder.genericBeanDefinition(ServiceA.class).getBeanDefinition());
​
        //Trigger initialization of all singleton bean s
        factory.preInstantiateSingletons(); //@1
​
        System.out.println("Destroy serviceA1"); 
        //Destroy the specified bean
        factory.destroySingleton("serviceA1");//@2
​
        System.out.println("Trigger all singletons bean Destruction of");
        factory.destroySingletons();
    }
}

The above two methods are used to trigger bean destruction [@ 1 and @ 2]

Run output

create class com.javacode2018.lesson002.demo13.ServiceA
create class com.javacode2018.lesson002.demo13.ServiceA
create class com.javacode2018.lesson002.demo13.ServiceA
 Destroy serviceA1
 Ready to destroy bean: serviceA1
 Trigger all singletons bean Destruction of
 Ready to destroy bean: serviceA3
 Ready to destroy bean: serviceA2

You can see that the postProcessBeforeDestruction is called three times and three custom bean s are destroyed in turn

 

Case 2: the method that triggers the @ PreDestroy annotation is called

As mentioned above, this annotation is processed in the commonannotation BeanPostProcessor #postprocessbeforeconstruction, so you only need to add this to the BeanPostProcessor list.

Another class

package com.javacode2018.lesson002.demo13;
​
import javax.annotation.PreDestroy;
​
public class ServiceB {
    public ServiceB() {
        System.out.println("create " + this.getClass());
    }
​
    @PreDestroy
    public void preDestroy() { //@1
        System.out.println("preDestroy()");
    }
}

@1: Annotated with @ PreDestroy annotation

test case

@Test
public void test2() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
​
    //Add a custom DestructionAwareBeanPostProcessor
    factory.addBeanPostProcessor(new MyDestructionAwareBeanPostProcessor()); //@1
    //Add CommonAnnotationBeanPostProcessor
    factory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor()); //@2
​
    //Inject bean s into the container
    factory.registerBeanDefinition("serviceB", BeanDefinitionBuilder.genericBeanDefinition(ServiceB.class).getBeanDefinition());
​
    //Trigger initialization of all singleton bean s
    factory.preInstantiateSingletons();
​
    System.out.println("Destroy serviceB");
    //Destroy the specified bean
    factory.destroySingleton("serviceB");
}

@1: Put in a custom DestructionAwareBeanPostProcessor

@2: Put in the CommonAnnotationBeanPostProcessor, which will handle the method marked with @ PreDestroy annotation in the bean

Run output according to the effect

create class com.javacode2018.lesson002.demo13.ServiceB
 Destroy serviceB
 Ready to destroy bean: serviceB
preDestroy()

 

Case 3: take a look at the execution sequence of the destruction phase

In fact, some common and necessary beannpostprocessors in spring have been automatically assembled into the beanPostProcessors list within ApplicationContext. For example, we are familiar with the following:

1.org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
  Used to handle@Resource,@PostConstruct,@PreDestroy of
2.org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
  Used to handle@Autowired,@Value annotation
3.org.springframework.context.support.ApplicationContextAwareProcessor
  Used for callback Bean Various implemented Aware Interface

Therefore, destroying bean s through ApplicationContext will trigger the execution of the method in 3.

Now let's demonstrate the destruction operation with AnnotationConfigApplicationContext.

Let's have a class

package com.javacode2018.lesson002.demo14;
​
import org.springframework.beans.factory.DisposableBean;
​
import javax.annotation.PreDestroy;
​
public class ServiceA implements DisposableBean {
​
    public ServiceA() {
        System.out.println("establish ServiceA example");
    }
​
    @PreDestroy
    public void preDestroy1() {
        System.out.println("preDestroy1()");
    }
​
    @PreDestroy
    public void preDestroy2() {
        System.out.println("preDestroy2()");
    }
​
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean In the interface destroy()");
    }
​
    //Custom destruction method
    public void customDestroyMethod() { //@1
        System.out.println("I am a custom destruction method:customDestroyMethod()");
    }
}

Two methods in the above class are marked with @ PreDestroy

This class implements the DisposableBean interface and overrides the destroy method in the interface

@1: For this destroyMethod, we will specify it as a custom method through @ Bean annotation.

Look at the test cases

package com.javacode2018.lesson002.demo14;
​
import org.junit.Test;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
​
@Configurable
public class DestroyTest {
​
    @Bean(destroyMethod = "customDestroyMethod") //@1
    public ServiceA serviceA() {
        return new ServiceA();
    }
​
    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(DestroyTest.class);
        //Start container
        System.out.println("Ready to start container");
        context.refresh();
        System.out.println("Container startup completed");
        System.out.println("serviceA: " + context.getBean(ServiceA.class));
        //Close the container
        System.out.println("Ready to close container");
        //Calling the close method of the container will trigger the bean destruction operation
        context.close(); //@2
        System.out.println("Container closed");
    }
}

The above class is marked with @ Configuration, indicating that it is a Configuration class, and there is a method marked with @ bean inside, indicating that this method is used to define a bean.

@1: Specify customDestroyMethod as the custom destruction method through the destroyMethod attribute

@2: Close the container and trigger the bean destruction operation

To run test1 and output

Ready to start container
 establish ServiceA example
 Container startup completed
serviceA: com.javacode2018.lesson002.demo14.ServiceA@243c4f91
 Ready to close container
preDestroy1()
preDestroy2()
DisposableBean In the interface destroy()
I am a custom destruction method:customDestroyMethod()
Container closed

You can see the order of destruction method calls:

  1. @All methods of PreDestroy annotation

  2. destroy() in DisposableBean interface

  3. Custom destruction method

Let's talk about a very, very important class. Cheer up and be sure to pay attention.

 

AbstractApplicationContext class (very important class)

Take a look at the UML diagram:

 

BeanFactory interface

We are already familiar with the top-level interface of Bean factory

 

DefaultListableBeanFactory class

It implements the BeanFactory interface. It can be said that this can be the only real implementation of the BeanFactory interface, which truly implements all the code in the bean life cycle.

Other classes rely on the DefaultListableBeanFactory class to forward requests to the DefaultListableBeanFactory for bean processing.

 

Other 3 classes

We often use these three classes: AnnotationConfigApplicationContext/ClassPathXmlApplicationContext/FileSystemXmlApplicationContext. Their main internal functions depend on their parent class AbstractApplicationContext, so we mainly look at AbstractApplicationContext.

 

AbstractApplicationContext class

There are two important methods in this class

public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)

Have you noticed that when we use AnnotationConfigApplicationContext, we often call the reflush method, and the above two methods will be called inside this method.

The first method: getBeanFactory()

Returns the ConfigurableListableBeanFactory in the current application context, which is also an interface type. This interface has a unique implementation class: DefaultListableBeanFactory.

Are you familiar with it? As mentioned above, DefaultListableBeanFactory is the only real implementation of BeanFactory.

This ConfigurableListableBeanFactory will be used in the application launch document to operate the spring container.

The second method: registerBeanPostProcessors

To put it mildly: this method is to register the BeanPostProcessor in the ConfigurableListableBeanFactory. The content will get all types of beanpostprocessors from the spring container and add them to the DefaultListableBeanFactory#beanPostProcessors list

Take a look at the source code of this method:

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

The request is forwarded to the postprocessor registrationdelegate#registerbeanpostprocessors.

The internal is relatively long. You can take a look at the source code. This method mainly uses four List collections of BeanPostProcessor type.

List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> orderedPostProcessors
List<BeanPostProcessor> nonOrderedPostProcessors;
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();

Let's start with: when the method is, all beans have been registered in the spring container.

spring will find out the list of all types of beanpostprocessors from the container, and then put them into the above four collections according to the following rules. The beanpostprocessors in the above four collections will be added to the DefaultListableBeanFactory#beanPostProcessors list in turn. Let's take a look at the beanpostprocessors in the four collections:

priorityOrderedPostProcessors (BeanPostProcessor with specified priority)

Implementation org springframework. core. The priorityordered interface has a BeanPostProcessor, but does not contain a of type MergedBeanDefinitionPostProcessor

orderedPostProcessors (BeanPostProcessor with order specified)

Annotated with @ Order annotation, or implemented org springframework. core. annotation. The BeanPostProcessor of the Order interface, but does not contain the type of MergedBeanDefinitionPostProcessor

nonOrderedPostProcessors (BeanPostProcessor without order specified)

The type in the above 2 is set to and other than MergedBeanDefinitionPostProcessor

internalPostProcessors

List of beanpostprocessors of type MergedBeanDefinitionPostProcessor.

You can take a look at CommonAnnotationBeanPostProcessor and AutowiredAnnotationBeanPostProcessor. These two classes implement the PriorityOrdered interface, but they also implement the MergedBeanDefinitionPostProcessor interface, so they will eventually be thrown into the collection of internalPostProcessors and placed at the end of BeanPostProcessor.

 

Bean life cycle flowchart

 

Spring learning suggestions

Here I list some ways to learn spring.

Look at the official documents

All the knowledge points in spring are available on the official website. The quality is very high. There may be some requirements for English, but you can use the translation software to turn it over.

Official website address:

https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/

Keywords: Java Spring bean

Added by brcolow on Wed, 02 Feb 2022 06:10:48 +0200