Sping IOC 源码剖析
Posted 怀瑾Hello World
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sping IOC 源码剖析相关的知识,希望对你有一定的参考价值。
目录
0.看源码的方式
- 好处:提高培养代码架构思维、深入理解框架
- 原则:
- 定焦原则:抓主线
- 宏观原则:站在上帝视角,关注源码结构和业务流程(淡化具体某条代码的编写细节)
- 读源码的方法和技巧
- 断点(观察调用栈)
- 反调(Find Useages)
- 经验(Spring 框架中doXXX,做具体处理的地方)
- Spring源码构建
- 参考spring-framework-5.1.x 源码编译 环境搭建 [ idea:2020.1 ]
- 下载源码(github)
- 安装gradle5.6.3(类似maven)、idea2019.1(或以上)、jdk11.0.5
- 导入(耗费一定时间)
- 编译工程(顺序:core-oxm-context-beans-aspects-aop)
- 工程->task->compileTestJava
1.Spring IOC初始化的主体流程
1.1.Spring IOC的容器体系
- BeanFactory:顶级容器接口类,定义了所有IOC容器必须遵从的一套原则
- ApplicationContext:增加额外的功能
- ClassPathXmlApplicationContext:包含解析xml等一系列内容
- AnnocationConfigApplicationContext:包含解析注解等一系列内容
- …(BeanFactory其他子接口/实现类)
- ApplicationContext:增加额外的功能
1.2.Bean生命周期关键时机点
1.2.1.Bean生命周期图解
比如通过反射实例化对象
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揭秘4某些无法注册到IOC容器的对象如何交给spring托管