There are 13 links in the Spring bean life cycle
-
Stage 1: Bean meta information configuration stage
-
Stage 2: Bean meta information parsing stage
-
Phase 3: register the Bean into the container
-
Phase 4: BeanDefinition merge phase
-
Phase 5: Bean Class loading phase
-
Stage 6: Bean instantiation stage (2 small stages)
-
Pre instantiation stage of Bean
-
Bean instantiation phase
-
-
Stage 7: BeanDefinition processing after merging
-
Stage 8: attribute assignment stage (3 small stages)
-
Post instantiation stage of Bean
-
Stage before Bean attribute assignment
-
Bean attribute assignment stage
-
-
Stage 9: Bean initialization stage (5 small stages)
-
Bean Aware interface callback phase
-
Pre Bean initialization phase
-
Bean initialization phase
-
Post Bean initialization phase
-
-
Stage 10: the stage after initialization of all singleton bean s is completed
-
Phase 11: Bean usage phase
-
Stage 12: pre Bean destruction stage
-
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:
-
Class is annotated with @ compound annotation to define a bean
-
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
-
Parsing of xml file definition bean
-
The properties file defines the parsing of the bean
-
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
-
Operation before Bean instantiation
-
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
-
Call the afterpropertieset method of the InitializingBean interface
-
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
-
Call org springframework. beans. factory. support. AbstractAutowireCapableBeanFactory#destroyBean
-
Call org springframework. beans. factory. config. ConfigurableBeanFactory#destroySingletons
-
Call the close method in ApplicationContext
The Bean destruction phase will be executed in turn
-
Poll the bean postprocessors list. If it is a destructionawarebeanepostprocessor of this type, it will call its internal postProcessBeforeDestruction method
-
If the bean implements org springframework. beans. factory. The disposablebean interface will call the destroy method in this interface
-
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:
-
@All methods of PreDestroy annotation
-
destroy() in DisposableBean interface
-
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/