Basic concepts of Spring startup process source code analysis

Posted by smeagal on Tue, 19 Oct 2021 20:16:38 +0200

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.

 

 

Topics: Java Spring