网上搜的Spring 生命周期,大部分都是写个demo,然后说按顺序输出就完事了
但是并没看到文章提起其位置,估计是这个问题太浅显了
我debug了一下 Spring 核心的onRefresh方法,结合以前的一些猜测,大致确认ConfigurableListableBeanFactory#createBean
是Bean创建的入口
具体的生命周期的流转如下图所示
Spring Bean生命周期 单例.png其实大家也可以参照网上的那些文章,直接打断点看堆栈,就可以得到具体的执行位置
Spring中的循环依赖
循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。
在这里先解释一下,Spring中用了三层缓寸来处理循环依赖
- 1级 singletonFactories : 单例对象的cache
- 2级 earlySingletonObjects :提前暴露的单例对象的Cache
- 3级 singletonFactories : 单例对象工厂的cache
Spring会根据1->3的顺序进行Bean的获取
在上图的 populateBean
方法前,其实有如下一段代码判断是否存在循环依赖
// 若 RootBeanDefinition 是 单例 且 允许容器尝试解决循环依赖问题
// 且 singletonsCurrentlyInCreation的map中包含当前的BeanName
boolean earlySingletonExposure = mbd.isSingleton()
&& this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
// 如果需要提前暴露
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName
+ "' to allow for resolving potential circular references");
}
// 将其加入第三级缓存
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
Object exposedObject = bean;
让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。——来源博客
后续还有一次循环依赖检查,检查提前暴露的对象是否被动态代理等修改为新的对象,如果有则报错
这里有篇文章可以参考
传送门
网友评论