This article is to understand the Spring startup process step by step by reading the configuration class through the AnnotationConfigApplicationContext.
Before looking at the source code, we need to know the role of some classes, which is more convenient for subsequent understanding.
1,BeanDefinition
BeanDefinition is the definition of a bean. It is used to describe a bean. It stores a series of information about the bean, such as the scope of the bean, the class corresponding to the bean, whether it is lazy to load, etc. the relationship between BeanDfinition and the bean can be regarded as the relationship between class and class. Some people say that it's good to have a class object, but class can't completely abstract the bean, For example, class cannot abstract the injection model of a bean, whether it is lazy to load, whether it is a factory bean, etc., so we need a BeanDefinition to describe a bean. In Spring, we can define a bean through < bean > < bean / >, @ Component and BeanDefinition
//Define a BeanDefinition AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); //Set the class of the current bean. 1. Get through class. 2. Get through the fully qualified class name of the class //beanDefinition.setBeanClass(Testt.class); beanDefinition.setBeanClassName("com.beans.Testt"); //Register beanDefinition in BeanFactory DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition("BEAN",beanDefinition); //Get Bean factory.getBean("BEAN");
You can also directly use RootBeanDefinition to obtain BeanDefinition
//Generate BeanDefinition RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Testt.class); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); //Register BeanDefinition in the factory factory.registerBeanDefinition("tt",rootBeanDefinition); Object tt = factory.getBean("tt"); System.out.println(tt);
Through the above three methods, we can define a Bean.
Suppose we have an entity class Testt
public class Testt { public String name; public void sayHello(){ System.out.println("Hello World!"); } public void setName(String name){ this.name = name; } public String getName(){ return name; } }
We can also assign a value to the name attribute through beanDefinition
//Generate a BeanDefinition RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Testt.class); //The assigned attribute name is 123 rootBeanDefinition.getPropertyValues().addPropertyValue("name","123"); //Get the value of name Object name = rootBeanDefinition.getPropertyValues().getPropertyValue("name").getValue(); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition("tt",rootBeanDefinition); Testt tt = (Testt) factory.getBean("tt"); System.out.println(tt); //Get the value of name through the instance String name1 = tt.getName(); System.out.println(name1); //123 System.out.println(name);//123
BeanDefinition is OK
beanDefinition.setScope("prototype"); // Set scope beanDefinition.setInitMethodName("init"); // Set initialization method beanDefinition.setAutowireMode(0); // Set auto assembly model 0. The default assembly mode is not auto injection, 1 ByName 2 ByType 3 constructor injection ......
Beandefinition also has many functions, which will not be explained one by one here. Interested readers can understand beandefinition one by one by looking at its interface and implementation class.
No matter which way you define a Bean, it will be resolved into a BeanDefinition object. In short, the relationship between a Bean and a BeanDefinition can be regarded as the relationship between a class and a class, so it's easy to understand it.
Here is a mergedBeanDefinitions. Let's talk about what it does. mergedBeanDefinitions is a concurrent HashMap that stores the merged BeanDefinition. Spring will merge the BeanDefinition and create a Bean based on the merged BeanDefinition
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
So when will beanDefinition merge? Let's give the following example to show you the truth
First, write a Spring.xml. We declare the bean through the xml 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.xsd"> <bean id="parent" scope="prototype"/> <bean id="children" parent="parent"/> </beans>
Here, we mean to set the parent's scope to prototype, but children do not set its scope attribute. The default is a singleton. Let's debug in the following way to see what exists in mergedBeanDefinitions
public class mainT { public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("Spring.xml"); classPathXmlApplicationContext.getBean("children"); } }
After debugging by setting a breakpoint on getBean, we can see the parameters stored in mergedBeanDefinitions in FactoryBean, as shown in the following figure
The scope attribute of children also becomes prototype, which is the merged BeanDefinition. In fact, it is equivalent to including the attributes of the parent class when the child class inherits the parent class
Here, we will also record the significant difference between RootBeanDefinition and GenericBeanDefinition
It can be found that the SetParentName of GenericBeanDefinition is normal and can be added
public void setParentName(@Nullable String parentName) { this.parentName = parentName; }
RootBeanDefinition will report an error and return null directly
@Override public String getParentName() { return null; } @Override public void setParentName(@Nullable String parentName) { if (parentName != null) { throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference"); } }
2,beanFactory
From the name, this is a factory class, which is responsible for producing and managing beans. In Spring, BeanFactory is the core interface of IOC container. It has many responsibilities and functions. Its core implementation class is the DefaultListableBeanefauFactory class. The following figure is a UML diagram of the DefaultListableBeanefauFactory class.
DefaultListableBeanefauFactory implements many interfaces, which also shows that the DefaultListableBeanefauFactory class inherits many functions
We can do many things through beanfactory, such as:
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(Testt.class); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); //Register BeanDefinition factory.registerBeanDefinition("Testt",beanDefinition); //Register alias factory.registerAlias("Testt","T"); //Get bean Object alias = factory.getBean("T"); System.out.println(alias);//com.beans.Testt@6b1274d2 //Get Bean by type String[] beanNamesForType = factory.getBeanNamesForType(Testt.class); System.out.println(beanNamesForType[0]);//Testt //Get BeanDefinition BeanDefinition testt = factory.getBeanDefinition("Testt"); System.out.println(testt); //Generic bean: class [com.beans.Testt]; scope=; abstract=false; lazyInit=null; autowireMode=0; //dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; //initMethodName=null; destroyMethodName=null //Get the number of beandefinitions int beanDefinitionCount = factory.getBeanDefinitionCount(); System.out.println(beanDefinitionCount); //Gets the provider of the Bean ObjectProvider<Testt> beanProvider = factory.getBeanProvider(Testt.class); System.out.println(beanProvider); //org.springframework.beans.factory.support.DefaultListableBeanFactory$1@6a472554
wait.
ApplicationContext inherits the BeanFactory interface. It is a more advanced container of Spring and provides more useful functions. We can see that there is a property in the implementation class GenericApplicationContext of ApplicationContext
private final DefaultListableBeanFactory beanFactory;
Now that he has inherited the BeanFactory interface, why add a DefaultListableBeanFactory attribute? You can see from the above that DefaultListableBeanFactory implements many interfaces and has many functions. The purpose of doing so is to, Enable GenericApplicationContext to indirectly own those functions inherited by DefaultListableBeanFactory through DefaultListableBeanFactory without inheriting or implementing them. o
Here we need to explain that the alias and beanName are also stored through map, {alias name: beanName}, key is the alias and value is the bean name
3,BeanDefinitionReader
A class can be directly converted to BeanDefinition, and the annotations on the class will be parsed,
The annotations it can parse are: @ Conditional, @ Scope, @ Lazy, @ Primary, @ DependsOn, @ Role, @ Description
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); AnnotatedBeanDefinitionReader Reader = new AnnotatedBeanDefinitionReader(factory); /** * You can also use registerBean(Testt.class) or registerBean(Testt.class, "specify Bean name") **/ Reader.register(Testt.class); Object testt = factory.getBean("testt"); System.out.println(testt);
4,ClassPathBeanDefinitionScanner
This is not a BeanDefinitionReader, but its function is similar to that of BeanDefinitionReader. It can scan, scan a package path, and parse the scanned class. For example, if the @ Component annotation exists on the scanned class, the class will be parsed as a BeanDefinition
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(factory); //Get the number of classes under the package // int scan = classPathBeanDefinitionScanner.scan("com.beans"); // System.out.println(scan);//6 //Scan classes without @ Component annotation and register them in the container. Classes without Bean defined by annotation or other methods will not be added to the container //classPathBeanDefinitionScanner.addExcludeFilter(new AnnotationTypeFilter(Component.class)); //Scan the @ component annotated class and register it in the container classPathBeanDefinitionScanner.addIncludeFilter(new AnnotationTypeFilter(Component.class)); //Get bean Object bean = factory.getBean(BeanTest.class); System.out.println(bean);//com.beans.BeanTest@5ccddd20
5,ConditionEvaluator
ConditionEvaluator is an internal class in Spring. It provides the judgment Condition function of @ Condition annotation. See its shouldSkip method for details.
6,Aware
Aware translates to perception. Its purpose is to let users perceive some information, such as BeanNameAware
@Component public class User implements BeanNameAware { private String awareName; @Override public void setBeanName(String name) { this.awareName = name; } public String getAwareName(){ return awareName; } }
We write a User class to implement BeanNameAware, override its method setBeanName, and assign it to our awareName
@ComponentScan("com.beans") public class mainT { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(mainT.class); User bean = context.getBean(User.class); System.out.println(bean.getAwareName()); } }
The results show that the name of the bean is obtained.