Sping IOC 源码剖析

Posted 怀瑾Hello World

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sping IOC 源码剖析相关的知识,希望对你有一定的参考价值。

目录

0.看源码的方式

  • 好处:提高培养代码架构思维、深入理解框架
  • 原则:
    • 定焦原则:抓主线
    • 宏观原则:站在上帝视角,关注源码结构和业务流程(淡化具体某条代码的编写细节)
  • 读源码的方法和技巧
    • 断点(观察调用栈)
    • 反调(Find Useages)
    • 经验(Spring 框架中doXXX,做具体处理的地方)
  • Spring源码构建

1.Spring IOC初始化的主体流程

1.1.Spring IOC的容器体系

  • BeanFactory:顶级容器接口类,定义了所有IOC容器必须遵从的一套原则
    • ApplicationContext:增加额外的功能
      • ClassPathXmlApplicationContext:包含解析xml等一系列内容
      • AnnocationConfigApplicationContext:包含解析注解等一系列内容
    • …(BeanFactory其他子接口/实现类)

1.2.Bean生命周期关键时机点

1.2.1.Bean生命周期图解

比如通过反射实例化对象

prototype singleton 销毁 销毁 第1步:实例化Bean 第2步:设置属性值 第3步:调用BeanNameAware的SetBeanName方法 第4步:调用BeanFactoryAware的setBeanFactory方法 第5步:调用ApplicationContextAware的setAppliactionContext方法 第6步:调用BeanPostProcessor的预初始化方法 第7步:调用InitializingBean的afterPropertiesSet方法 第8步:调用定制的初始化方法init-method 第9步:调用BeanPostProcessor的后初始化方法 将准备就绪的Bean交给调用者 Spring缓存池中准备就绪的Bean 调用DisposableBean的destory方法 调用destory-method属性配置的销毁方法
Bean生命周期的整个执行过程描述:
1.根据配置情况调用Bean构造方法或工厂方法实例化Bean
2.利用依赖注入完成Bean中所有属性值的配置注入
3.如果Bean实现了BeanNameAware接口,则Spring调用Bean的setBeanName()方法传入当前Bean的id值
4.如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory()方法传入当前工厂实例的引用
5.如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext()方法传入当前ApplicationContext实例的引用
6.<如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialzation()对Bean进行加工操作,此处非常重要,Spring的AOP就是利用它实现的>
7.如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet()方法
8.如果在配置文件中通过init-method属性指定了初始化方法,则调用该初始化方法
9.<如果BeanPostProcessor和Bean关联,则Spring将调用该接口的初始化方法postProcessAfterInitialzation(),此时,Bean已经可以被应用系统使用了>
10.如果在<bean>中指定了该Bean的作用范围scope="singleton",则该Bean将放入Spring IOC的缓存池中,将触发Spring 对该Bean的生命周期管理;如果在<bean>中指定了该属性的作用范围scope="prototype",则将该Bean交给调用者
11.如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则Spring将调用该方法对Bean进行销毁
<注意:Spring为Bean提供了细致全面的生命周期过程,通过实现特定的接口或bean的属性值设置,都可以对Bean的生命周期过程产生影响,虽然可以随意配置bean的属性值,但是建议不要过多的使用Bean实现接口,因为这样会导致代码和Spring的聚合过于紧密>

1.2.2.BeanDefinition结构

1.2.3.Bean⽣命周期关键时机点断点分析

  • 思路:创建⼀个类 LagouBean ,让其实现⼏个特殊的接⼝,并分别在接⼝实现的构造器、接⼝⽅法中
    断点,观察线程调⽤栈,分析出 Bean 对象创建和管理关键点的触发时机。
  • LagouBean
import org.springframework.beans.factory.InitializingBean;

public class LagouBean implements InitializingBean 

	private Integer id;
	private String message;

	public LagouBean() 
		System.out.println("构造函数...");
	

	@Override
	public void afterPropertiesSet() throws Exception 
		System.out.println("初始化....");
	


  • BeanPostProcessor 接⼝实现类
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * @Author 应癫
 * @create 2019/12/3 16:59
 */
public class MyBeanPostProcessor implements BeanPostProcessor 

	public MyBeanPostProcessor() 

		System.out.println("BeanPostProcessor 实现类构造函数...");
	

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException 
		if("lagouBean".equals(beanName)) 
			System.out.println("BeanPostProcessor 实现类 postProcessBeforeInitialization 方法被调用中......");
		
		return bean;
	

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException 
		if("lagouBean".equals(beanName)) 
			System.out.println("BeanPostProcessor 实现类 postProcessAfterInitialization 方法被调用中......");
		
		return bean;
	

  • BeanFactoryPostProcessor 接⼝实现类
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

/**
 * @Author 应癫
 * @create 2019/12/3 16:56
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor 

	public MyBeanFactoryPostProcessor() 

		System.out.println("BeanFactoryPostProcessor的实现类构造函数...");
	

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
		System.out.println("BeanFactoryPostProcessor的实现方法调用中......");
	

  • applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="
	    http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
">

	<bean id="lagouBean" class="LagouBean"></bean>

	<bean id="myBeanFactoryPostProcessor" class="MyBeanFactoryPostProcessor"/>
	<bean id="myBeanPostProcessor" class="MyBeanPostProcessor"/>

</beans>
  • IoC 容器源码分析⽤例
	@Test
	public void testGetBean()
		// ApplicationContext是容器的高级接口,BeanFactory(顶级容器/根容器,规范了/定义了容器的基础行为)
		// 叫做单例池,singletonObjects,容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程)

		// 反向观察bean的调用,通过debug查看调用栈
		// bean对象的构造方法、初始化方法、 bean后置处理器before/after方法: org.springframework.context.support.AbstractApplicationContext#refresh#finishBeanFactoryInitialization
		// beanFactory后置处理器构造方法、postProcessBeanFactory方法:org.springframework.context.support.AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
		// bean后置处理器构造方法: org.springframework.context.support.AbstractApplicationContext#refresh#registerBeanPostProcessors
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
		LagouBean lagouBean = (LagouBean) applicationContext.getBean("lagouBean");
		System.out.println(lagouBean);
	
  • (1) 分析 Bean 的创建是在容器初始化时还是在 getBean 时

    所以:未设置延迟加载的前提下,Bean 的创建是在容器初始化过程中完成的

  • (2)分析构造函数调⽤情况

    通过如上观察,我们发现构造函数的调⽤时机在AbstractApplicationContext类refresh⽅法的
    finishBeanFactoryInitialization(beanFactory)处;

  • (3)分析 InitializingBean 之 afterPropertiesSet 初始化⽅法调⽤情况观察
    InitializingBean中afterPropertiesSet ⽅法的调⽤时机也是在AbstractApplicationContext类refresh⽅法的finishBeanFactoryInitialization(beanFactory);

  • (4)分析BeanFactoryPostProcessor 初始化和调⽤情况
    分别在构造函数、postProcessBeanFactory ⽅法处打断点,观察调⽤栈,发现
    BeanFactoryPostProcessor 初始化在AbstractApplicationContext类refresh⽅法的
    invokeBeanFactoryPostProcessors(beanFactory);
    postProcessBeanFactory 调用
    在AbstractApplicationContext类refresh⽅法的
    invokeBeanFactoryPostProcessors(beanFactory);

  • (5)分析 BeanPostProcessor 初始化和调⽤情况
    分别在构造函数、postProcessBeanFactory ⽅法处打断点,观察调⽤栈,发现
    BeanPostProcessor 初始化在AbstractApplicationContext类refresh⽅法的
    registerBeanPostProcessors(beanFactory);
    postProcessBeforeInitialization 调用在AbstractApplicationContext类refresh⽅法的
    finishBeanFactoryInitialization(beanFactory);
    postProcessAfterInitialization 调用在AbstractApplicationContext类refresh⽅法的
    finishBeanFactoryInitialization(beanFactory);

  • 总结
    根据上⾯的调试分析,我们发现 Bean对象创建的⼏个关键时机点代码层级的调⽤都在AbstractApplicationContext 类 的 refresh ⽅法中,可⻅这个⽅法对于Spring IoC 容器初始化来说相当关键,汇总如下:

关键点触发代码
构造器refresh#finishBeanFactoryInitialization(beanFactory)
BeanFactoryPostProcessor 初始化refresh#invokeBeanFactoryPostProcessors(beanFactory)
BeanFactoryPostProcessor ⽅法调用refresh#invokeBeanFactoryPostProcessors(beanFactory)
BeanPostProcessor 初始化registerBeanPostProcessors(beanFactory)
BeanPostProcessor ⽅法调⽤refresh#finishBeanFactoryInitialization(beanFactory)

1.3.Spring IOC初始化主流程

  • 由上分析可知,Spring IoC 容器初始化的关键环节就在 AbstractApplicationContext#refresh() 方法中,我们查看 refresh 方法来俯瞰容器创建的主体流程,主体流程下的具体⼦流程我们后⾯再来讨论。
	@Override
	public void refresh() throws BeansException, IllegalStateException 
		synchronized (this.startupShutdownMonitor) 
			/*第一步:刷新前的预处理
				表示在真正做refresh操作之前需要准备做的事情;
				设置Spring容器的启动时间
				开启活跃状态、撤销关闭状态
				验证环境信息里一些必须存在的属性等
			 */
			prepareRefresh();

			/* 第二步:Tell the subclass to refresh the internal bean factory.
			   获取BeanFactory;默认实现是DefaultListableFactory
			   加载BeanDefition 并注册到 BeanDefitionRegistry
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 第三步:BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
			prepareBeanFactory(beanFactory);

			try 
				// 第四步:BeanFactory准备⼯作完成后进⾏的后置处理⼯作
				postProcessBeanFactory(beanFactory);

				// 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执
				registerBeanPostProcessors(beanFactory);

				// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
				initMessageSource();

				// 第⼋步:初始化事件派发器
				initApplicationEventMulticaster();

				// 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
				onRefresh();

				// 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
				registerListeners();

				/*
				第⼗⼀步:
				初始化所有剩下的⾮懒加载的单例bean
				初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
				填充属性
				初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
				调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
				*/
				finishBeanFactoryInitialization(beanFactory);

				/*
				第⼗⼆步:
				完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事
				件 (ContextRefreshedEvent)
				*/
				finishRefresh();
			
	...
	

2.BeanFactory创建流程

2.1 获取BeanFactory⼦流程

时序

以上是关于Sping IOC 源码剖析的主要内容,如果未能解决你的问题,请参考以下文章

Sping IOC 源码剖析

sping揭秘4某些无法注册到IOC容器的对象如何交给spring托管

Spring框架介绍及使用

Spring IOC源码剖析:Spring IOC容器初始化主体流程

Spring源码剖析-IOC启动流程

Spring Ioc源码深入剖析预备知识