[spring source code series-01] overall overview of spring underlying source code

The overall column of the Spring source code series


content link address
[1] Overall overview of spring source code https://blog.csdn.net/zhenghuishengq/article/details/130940885

1. Overall overview of spring source code

1. Preliminary Overview

Before analyzing the source code, you first need to go to the official website to download the spring source package, it is recommended to download the 5.xx version. What I installed here is: https://github.com/spring-projects/spring-framework/tree/5.2.x

In our cognition, the spring framework is a framework and an ecology. In spring, there are two main parts: one is IOC and the other is AOP. IOC is called inversion of control, which is an idea, which is to hand over objects to container management, and its counterpart is DI, which is called dependency injection, which is a specific implementation of IOC. AOP is a sublimation of the IOC foundation, so it is necessary to understand AOP after a thorough understanding of IOC.

IOC is generally used as a container of bean objects. The basic process of constructing objects is as follows. Some extension points are omitted in the middle, such as some post-processors, enhancers, etc., and will be added one by one in the subsequent sequence. The following picture is mainly for the flow chart of obtaining beans in the way of annotation.
insert image description here

1. The acquisition context is generally obtained in two ways, one is obtained through XML, and the other is obtained through annotations. XML is rarely used in the popular springboot, so here we also choose to use annotations way to get this context ApplicationContext.

public class MainClass {
    
    
	public static void main(String[] args) {
    
    
		AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(MainCofig.class);
		User user = (User)context.getBean("user");
		System.out.println("user的类型:"+user.getClass());
	}
}

@Configuration
@ComponentScan(basePackages = {
    
    "com.zhs.study"})
public class MainCofig {
    
    
}

Of course, you can also get the context through this XML method. When you first contact spring, you unload the bean in the xml file.

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("xxx.xml");

2. Regardless of whether they use this XML configuration file or use this annotation to obtain the context, they have to generate a BeanDefinition bean definition before calling getBean. After having a specific bean definition, they can go through this bean factory. Produce or obtain this bean object. Before obtaining a unified BeanDefinition, it is necessary to convert different context configuration information into a unified BeanDefinition by implementing the BeanDefinitionReader interface

public interface BeanDefinitionReader {
    
    ...}

The BeanDefinitionReader interface has a specific implementation class, as shown in the figure below, with the above-mentioned xml and annotation-generated bean definition readers, etc., which is equivalent to a unified parser.

insert image description here

Then through a BeanDefinitionRegistry register, the read thing is registered as a BeanDefinition, so that a BeanDefinition object that can be recognized by a bean factory can be uniformly generated

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    
    
	try {
    
    
		Document doc = doLoadDocument(inputSource, resource);
		return registerBeanDefinitions(doc, resource);
	}
}

Since there are many generated beanDefinitions, the bean definition will be added to the beanDefinitionMap first.

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

3. Then the getBean() method will be called to obtain the value from the bean factory. If the bean definition exists, it will be obtained directly. If it does not exist, it will be created first and then the value will be returned.

Student student = (Student) context.getBean("student");

In the process of obtaining this bean, it will mainly go through three main processes of instantiation, property filling and initialization, which are the three main steps of instantiation, property filling and initialization . Instantiation is to open up a space in the heap memory and assign default values ​​to the property values ​​of the object; property filling is to set the final value in this object; and finally execute the init initialization method . And the order of these three steps cannot be changed.

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-tqAi96hL-1685408227594)(img/1685340071844.png)]

In the process of filling attributes to this initialization, some attributes of the Aware interface can be set, and the context can be obtained by implementing these Aware interfaces. If an object wants to obtain the information of the entire context, it can be achieved ApplicationContextAware. After obtaining this instance externally, the context can be obtained directly through the getApplicationContext method.

//实现上下文的aware接口
@Component
public class First implements ApplicationContextAware {
    
    
    private ApplicationContext applicationContext;
    //实现方法
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        this.applicationContext = applicationContext;
    }
    //将值返回
    private ApplicationContext getApplicationContext(){
    
    
        return applicationContext;
    }
}

The role of Aware is: when the bean object created by the Spring container is performing specific operations, if other objects in the container are needed, the object can implement the Aware interface at this time to meet the current needs.

In addition to setting Aware, you can also set some BeanPostProcessorprocessors. First, the BeanPostProcessor.before() method will be called, and finally the BeanPostProcessor.after() method will be called. Between these two processors, the init initialization method mentioned above is executed. And the methods of these two handlers are as follows

//前置处理器
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
	return bean;
}
//后置处理器
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
	return bean;
}

4. Then generate a complete bean object, store it in the concurrentHashMap first-level cache, and then directly obtain the instance in the map. This is a rough ioc process.

5. In the spring container, the spring bean object is divided into ordinary objects and container objects . The container object is equivalent to a built-in object, and the ordinary object is equivalent to a custom object. Built-in objects are similar to some classes that need to be loaded in advance, and some objects are not explicitly customized. Custom objects are some instance objects customized in xml or annotations.

2. Extension point mechanism

The overall process of IOC is roughly described above. In addition to the above components, there are some extension point mechanisms, such as some post-processors, PostProcessormainly BeanFactoryPostProcessor and BeanPostProcessor processors. The former is mainly used to enhance beanDefinition information Yes, the latter can be used to enhance bean information, such as aop, etc.

The interface information of BeanFactoryPostProcessor is as follows, and this interface is a functional interface. In the spring container, this interface exists in the form of a collection, that is, there will be multiple post-processors of this bean factory.

//函数式接口
@FunctionalInterface
public interface BeanFactoryPostProcessor {
    
    
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

The interface information of BeanPostProcessor is as follows. There are two methods in it, which are called the pre-method of the post-processor and the post-method of the post-processor.

public interface BeanPostProcessor {
    
    
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
		return bean;
	}

}

As in the following BeanFactoryPostProcessor example, after implementing this interface, rewriting the postProcessBeanFactory method inside can get beanDefination, and make an enhanced function for this bean definition. It can set some bean descriptions, add some description information, whether to set it as class loading, etc.

/**
 * 自定义bean工厂的后置处理器类
 * 对beanDefinition做增强功能
 * @author zhenghuisheng
 * @date : 2023/5/25
 */
@Component
public class BeanFactoryTest implements BeanFactoryPostProcessor {
    
    
    /**
     * @param beanFactory : beanDefinition
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    
    
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("taskTestController");
        taskTestController.setDescription("hhhhha");
        System.out.println("=================taskTestController增强完成!");
        System.out.println(taskTestController.getDescription());
    }
}

3. The core method refresh

In addition, there is an important method in the construction method of the xml method and the construction method of the annotation, and refreshtheir construction methods are as follows

//XML的方式获取到上下文
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    
    
	super(parent);   // 初始化父类 ,获得xml路径资源解析器
	setConfigLocations(configLocations);  // 通过环境变量解析 xml路径
	if (refresh) {
    
    
		refresh();   // 这个方法时spring是最终要的一个方法,甚至体系整个ioc的声明周期
	}
}

//注解的方式获取上下文
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    
    
	//调用构造函数
	this();
	//注册我们的配置类
	register(annotatedClasses);
	//IOC容器刷新接口
	refresh();
}

In this refresh method, it can be said that it is the core of the entire spring bottom layer, and its code is as follows. Therefore, the continued analysis of the spring source code in the following article basically revolves around the following methods.

@Override
public void refresh() throws BeansException, IllegalStateException {
    
    
	synchronized (this.startupShutdownMonitor) {
    
    
		//1:准备刷新上下文环境
		prepareRefresh();
		//2:获取告诉子类初始化Bean工厂  不同工厂不同实现
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		//3:对bean工厂进行填充属性
		prepareBeanFactory(beanFactory);
		try {
    
    
			// 第四:留个子类去实现该接口,bean工厂的后置处理器
			postProcessBeanFactory(beanFactory);
			// 调用我们的bean工厂的后置处理器. 
       	 	//1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用
			invokeBeanFactoryPostProcessors(beanFactory);
			// 注册我们bean的后置处理器
			registerBeanPostProcessors(beanFactory);
			// 初始化国际化资源处理器.
			initMessageSource();
			// 创建事件多播器
			initApplicationEventMulticaster();
			// 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
			onRefresh();
			//把我们的事件监听器注册到多播器上
			registerListeners();
			// 实例化我们剩余的单实例bean.
			finishBeanFactoryInitialization(beanFactory);
			// 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
			finishRefresh();
		}
    }
}

4. The difference between BeanFactory and FactoryBean

BeanFactoy is a large container interface, and ApplicationContext is its specific implementation interface, which is mainly used to create and obtain objects. When using this BeanFacory, you must follow the complete creation process, which is controlled and managed by spring.

FactoryBean only needs to call getObject to return a specific object. The entire object creation process is controlled by the user, which is more flexible and can also be used to create some special beans and more complex beans. In this FactoryBean object, there are three main methods, namely: IsSingleton, getObject, getObjectType these three methods, the most frequently used method is getObject.

In the method of preInstantiateSingletons , there is a judgment logic as follows to judge whether the bean is a factory bean

//是不是工厂bean
if (isFactoryBean(beanName)) {
    
    ...}

If the object is not obtained, the specific value of the object is invisible, because the object was not created in advance. Next, look at a piece of code, an entity class implements this bean factory, and then creates an entity class for teacher objects and student objects

/**
 * @author zhenghuisheng
 * @date : 2023/5/29
 */
@Component
public class TeacherFactory implements FactoryBean<Teacher> {
    
    
    @Override
    public Teacher getObject() throws Exception {
    
    
        return new Teacher();
    }

    @Override
    public Class<?> getObjectType() {
    
    
        return Teacher.class;
    }
}

Next, get the entity class in this factory through the context, and check the results as follows

//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");
//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");

&That is to say, the object returned by this FactoryBean is not the current object, and the original value can also be obtained through this , which can make the method of obtaining the value more flexible. The meaning of using this FactoryBean is: you only need to rewrite the getObject method to get the corresponding bean object, without fully complying with the life cycle of the bean

Guess you like

Origin blog.csdn.net/zhenghuishengq/article/details/130940885