准备工作
分析源码的好处
分析源码的原则
- 安装gradle
- 下载源码,github:https://github.com/spring-projects/spring-framework
- 导入:需要耗费很长的时间,本人花费大约22分钟
- 编译工程:编译顺序为core-oxm-context-beans-aspects-aop,编译的时候选择gradle->工程->tasks->other->compileTestJava
问题说明
本人使用的是5.2.2版本,遇到了两个问题:
- 遇到 Test events were not received 问题,解决方案参考:https://stackoverflow.com/questions/57795263/test-events-were-not-received-when-run-tests-using-intellij ,在idea中配置:File -> Settings ->Build,Execution, Deployment -> Build Tools -> Gradle -> run tests using中选择 使用idea,而不是使用gradle
- 遇到 NoClassDefFoundError: org/hamcrest/SelfDescribing 问题,这个是junit版本问题,我引入的是
``testCompile group: 'junit', name: 'junit', version: '4.12'
,修改为如下dependencies {
compile(project(":spring-context"))
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.6'
// testCompile group: 'junit', name: 'junit', version: '4.12'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
IOC容器初始化的主体流程
容器体系
先说一下容器,spring ioc容器并不是map,map只是容器的一个成员而已,被称作单例池子,也就是scope属性为singleton的bean对象才会放在map中被容器管理,容器是一组组件和过程的集合,包括了前面说的生命周期,例如BeanFactory、单例池、BeanPostProcessor等以及之间的协作。
这里主要说明IOC容器的继承体系,如图,这里展示了部分,是以ApplicationContext接口,因为我们最常用的就是这个了:
最顶层的容器接口是 BeanFactory,它定义了容器的最基本的行为,并不是全部,这也是Spring设计的优雅的地方,将不同的功能封装在不同的接口:
- BeanFactory:顶层接口,提供了最基础的行为
- getBean:重载的getBean方法用来获取容器中的对象
- getBeanProvider:重载方法,放回指定Bean的提供程序,也就是获取是谁创建了这个Bean
- containsBean:判断当前容器是否存在bean
- isSingleton:判断Bean是否为单例Bean
- isPrototype:判断Bean是否为多例Bean
- isTypeMatch:判断获取的Bean是否为指定类型
- getType:获取Bean的类型
- getAliases:获取别名
- FACTORY_BEAN_PREFIX静态属性:用来获取 FactoryBean 对象,因为我们在使用 FactoryBean对象创建Bean的时候直接会获取创建的Bean对象,而不是 BeanFactory对象
- HierarchicalBeanFactory接口:提供用来获取父BeanFactory的接口,获取父工厂
- ListableBeanFactory接口:提供的集合相关的Bean接口,需要的方法全部都是集合相关,比如getBeanNamesForType用来获取某个属性的全部Bean
- ResourceLoader接口:不属于容器接口,是加载资源的接口
- EnvironmentCapable接口:国际化接口
- MessageSource接口:消息定义接口
- ApplicationEventPublisher接口:监听接口
处理器以及加载机制
加载机制
Bean的构造器方法
构造器执行被 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)
Bean的初始化方法,也就是InitializingBean
初始化方法被 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)
BeanFactoryPostProcessor处理器
初始化方法,也就是构造器 AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory)
方法的执行 AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory)
BeanPostProcessor处理器
初始化方法 AbstractApplicationContext#refresh#registerBeanPostProcessors(beanFactory)
前置方法的执行 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)
后置方法的执行 AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory)
上面是Bean的生命周期涉及的一些执行流程和处理器,可以看到斗鱼AbstractApplicationContext的refresh方法相关,下面看看这个方法干了什么?
refresh方法的理解
完成ioc容器初始化的一个核心方法,该方法可被在执行过程中调用来刷新容器。
@Override
public void refresh() throws BeansException, IllegalStateException {
// 对象锁,防止在初始化的时候被关闭或者是关闭的时候刷新容器
synchronized (this.startupShutdownMonitor) {
// 容器初始化的准备工作
// Prepare this context for refreshing.
prepareRefresh();
// 初始化Bean工厂,默认实现是DefaultListableBeanFactory
// 并且完成加载 BeanDefinition, 加载调用的方法为loadBeanDefinitions(beanFactory); 并且注册到 BeanDefinitionRegistry
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// BeanFactory的初始化配置
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// 后置处理
// postProcessBeanFactory为扩展口
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// 实例化BeanFactory的后置处理器,并调用后置处理器的方法
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor后置处理器,在创建Bean的前后执行
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// 初始化国际化等信息
// Initialize message source for this context.
initMessageSource();
// 初始化事件分发器
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 自定义刷新逻辑
// Initialize other special beans in specific context subclasses.
onRefresh();
// 注册监听器,实现了 ApplicationListener 的监听器
// Check for listener beans and register them.
registerListeners();
// 调用方法,初始化并创建单例Bean,并且设置属性
// 以及调用各种初始化方法:比如说 InitializingBean的初始化,init-method等
// 调用后置处理器的方法,对实例Bean进行前置和后置处理
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// 完成刷新
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
整个大致的流程为:
初始化BeanFactory -> 初始化BeanFactory的处理器,并执行处理器方法 -> 注册Bean的后置处理器 -> 实例化Bean信息、完成初始化信息、执行Bean的后置处理器
BeanFactory的创建流程
从refresh开始,在 obtainFreshBeanFactory 该方法中我们完成了工厂的创建以及BeanDefinition的创建,这里说下流程
整个流程如图所示,里面代码相对简单,也就是判断当前是否有工厂,有就销毁重新创建,没有则创建。
BeanDefinition的加载解析以及注册
这个流程是层层调用,下面来一个文字流程
- AbstractApplicationContext#refresh开始,还是创建工厂的方法 obtainFreshBeanFactory
- AbstractApplicationContext#obtainFreshBeanFactory中调用refreshBeanFactory()方法
- AbstractRefreshableApplicationContext#refreshBeanFactory调用loadBeanDefinitions
- AbstractXmlApplicationContext#loadBeanDefinitions方法
- AbstractXmlApplicationContext#loadBeanDefinitions,重载方法的调用
- AbstractBeanDefinitionReader#loadBeanDefinitions
- AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)
- AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set
) - AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource…)
- XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
- XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
- XmlBeanDefinitionReader#doLoadBeanDefinitions
- XmlBeanDefinitionReader#registerBeanDefinitions
- DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
- DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
- DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
- DefaultBeanDefinitionDocumentReader#parseDefaultElement
- DefaultBeanDefinitionDocumentReader#processBeanDefinition
- 解析xml中的Bean元素,封装为BeanDefinition
- BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element)
- BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
- 注册BeanDefinition
- BeanDefinitionReaderUtils#registerBeanDefinition
- DefaultListableBeanFactory#registerBeanDefinition
- 解析xml中的Bean元素,封装为BeanDefinition
整个调用链就是这样的,整个流程就是将xml解析为Resource资源,将Resource资源转化为Document对象,然后解析document对象,针对每一个标签进行处理,这里再5.2版本是针对四个标签进行了处理,分别是import、bean、beans、alias,之后对于document中的元素进行解析,封装成为BeanDefinition对象,最后将BeanDefinition放入beanDefinitionMap中。
这就是整个解析和注册的流程,注册其实就是放入map中。
Bean对象的创建流程
继续是在AbstractApplicationContext中的refresh方法来,前面说过具体的解析所调用的方法是 finishBeanFactoryInitialization
- AbstractApplicationContext#finishBeanFactoryInitialization
- DefaultListableBeanFactory#preInstantiateSingletons : 只加载单例Bean
- AbstractBeanFactory#getBean(java.lang.String)
- AbstractBeanFactory#doGetBean
- DefaultSingletonBeanRegistry#getSingleton
- AbstractAutowireCapableBeanFactory#createBean
- AbstractAutowireCapableBeanFactory#doCreateBean
- AbstractAutowireCapableBeanFactory#initializeBean
initializeBean这个是最重要的初始化方法,里面包括初始化方法、后置处理器等的执行。
循环依赖问题
循环依赖问题是对象之间相互依赖的一种现象,比如说对象A依赖对象B,对象B依赖对象A,这种现象就叫做循环依赖。
根据Spring注入的方式有两种:
- 构造器初入
- setter注入
对于构造器注入来说,循环依赖是解决不了的,Spring只能解决setter注入方法,并且Bean的scope属性必须是singleton的循环依赖问题。下面说处理方案就知道为什么了。
方案
对于循环依赖,Spring的解决方案是提前暴露Bean,通过三级缓存来实现
- 一级缓存:singletonObjects,单例池
- 二级缓存:earlySingletonObjects
- 三级缓存:singletonFactories
如何做的呢?就以A、B为例,根据我们前面所说的对象生命周期,Bean初始化的第一步就是实例化Bean,接下来会设置属性值,而设置属性值才会有循环依赖的问题,所以我们在实例化Bean之后就放入三级缓存中,之后在设置属性值的时候会发现依赖B,这个时候会加载B的Bean,初始化B设置属性值的时候发现依赖A,这个时候会从缓存中 A的实例,先从一级缓存,再从二级缓存,都没有发现会从三级缓存中拿,注意这个时候A对象是不完全的,仅仅实例化完成,属性还没有设置,之后就将实例化好的A从三级缓存中移除,然后放入二级缓存,B现在有了A的对象,就可以完成初始化了;B的初始化完成,接下来返回继续A的初始化,当A的生命周期完成之后就放入单例池,移除二级缓存中的A对象实例。
那为什么构造器注入无法解决呢?原因是三级缓存的实现建立在提前暴露Bean,构造器注入的话Bean就无法完成实例化,进一步也就没办法解决构造器注入的问题了。
至于为什么scope属性必须是singleton,原因是prototype属性的Bean不在Spring IoC容器的管理范围,只负责创建,不负责管理,所以也没办法解决。