Spring源码分析-05-循环依赖
本文主要是分析了Spring 是如何通过属性注入的方式解决循环依赖这个问题, 参考源码给出Spring生成 BeanA 对象的流程图(严格意义上讲不算是流程图). 建议读者在阅读本文时关注singletonObjects, singletonFactories, earlySingletonObjects 这三个对象.
PS: 本文需要读者对Spring 的启动流程, getBean()流程,以及创建对象creatBean()有一个大致的了解,若缺少前提知识,请出门左转仔细阅读笔者Spring系列文章.
测试案例:
循环依赖:
- 简单来讲: A中有B,B中有A ,两者相互引用, 即所谓的循环依赖问题.
A.b=B ,B.a=A 这个样子的案例
测试类:
public class TestCircularReference {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(MainOfCircularReference.class);
BeanA beanA = context.getBean(BeanA.class);
BeanB beanB = context.getBean(BeanB.class);
System.out.println("A: "+beanA.toString()+"./n B: "+beanB.toString());
System.out.println("=====================A.b=B? ");
System.out.println(beanA.getBeanB());
}
}
BeanA&&BeanB
- BeanA.class
@Component
public class BeanA {
public BeanB getBeanB() {
return this.beanB;
}
public void setBeanB(BeanB beanB) {
beanB = beanB;
}
public BeanA() {
}
@Autowired
private BeanB beanB;
public BeanA(BeanB beanB) {
beanB = beanB;
}
}
- BeanB.class
@Component
public class BeanB {
@Autowired
private BeanA beanA;
public BeanB(BeanA a) {
}
public BeanB() {
}
public BeanA getBeanA() {
return beanA;
}
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
}
测试案例:
构造器注入属性: Spring 不会解决这个问题, 会抛Exception
测试配置类:
- MainOfCircularReference.class
@Configuration
@ComponentScan("circular.reference")
public class MainOfCircularReference {
}
测试结果:
人话描述流程:
PS:这部分内容为了贴近程序运行流程,讲的有点乱,可以选择去看总结中我给出的流程,相对清晰一点点.
Spring IOC 创建BeanA流程如下:
尝试通过缓存获取BeanA 即 getSingleton(“BeanA,true) ,从singletonObjects, earlySingletonObjects, singletonFactories 获取
创建BeanA 即 doCreateBean()
- 实例化beanA 即 createBeanInstance()
- 为beanA设置属性 ,即 BeanA.beanB 即 popuplateBean()
- 通过缓存获取 BeanB , 即getSIngleton()
- 创建BeanB 即 doCreateBean()
- 实例化beanB , 即 createBeanInstance()
- 为beanB设置属性即BeanB.beanA , populateBean()
- 通过缓存获取beanA, 并设置为beanB的属性值
- 初始化BeanB, 即initializeBean()
- 返回 beanB 对象
- 将BeanB 加入缓存 singletonObjects中.
- 返回beanB
- 初始化 beanA , 即initializeBean()
- 通过二级缓存 获取beanA 并返回.即
Object earlySingletonReference = getSingleton(beanName, false);
将BeanA 加入缓存 即 singletonObjects中. addSingleton()
返回beanA.
看完上述流程, 是不是有一种我是谁,我在那,我在干什么的感觉, 这其实很正常(wuwuwu…). 以上内容虽然很多,但我们需要重点关注的内容也就几个内容. 在图中,笔者通过深色图案进行表述. 所以请一定要看图.
重点来了: 希望 读者在阅读本文时, 一定要看图, 笔者认为自己较为清晰的给出了整个调用栈的流程. 重点关注深色图案内容.
三级缓存:
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
通过判断缓存内容的变化来加深自己对Spring 解决循环依赖的理解.
源码分析:
- 在源码解析过程中,笔者只会给出自己认为重要的部分,读者在阅读本文的过程中,最好是实时debug ,并实时追踪 singletonObjects, earlySingletonObjects,singletonFactories, 以及singletonObjects.containsKey(“beanA”),singletonObjects.containsKey(“beanB”) 这几个值.
跳过 refresh()-> finishBeanFactoryInitialization()->getBean()-> doGetBean(XXX) 内容,从 doGetBean(“beanA”) 开始分析.
创建BeanA 对象
实例化BeanA
- doGetBean()->doCreateBean()->createBeanInstance()->addSIngletonFactory()
不重要跳过!
执行结果
BeanA属性赋值
- **即 设置BeanA.beanB属性值, 这个过程非常重要!!! ** .
有图可以知道, 要为beanA设置属性值BeanB, 因为BeanB 未创建,所以走
doGetBean()->doCreateBean()->createBeanInstance()->addSingletonFactory() 实例化beanB
为beanB属性属性赋值– BeanB.beanA, 走 populateBean()
实例化beanB
同实例化BeanA, 跳过部分内容,直到为beanB属性赋值,即 populateBean()过程.
BeanB属性赋值
- DefaultSingletonBeanRegistry.class
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);// 从单例bean集合中获取(缓存)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 判断当前bean是否正在创建
synchronized (this.singletonObjects) {// 如果一级缓存没有,并且当前bean正在被创建
singletonObject = this.earlySingletonObjects.get(beanName);// 尝试从二级缓存 earlySingletonObject中获取bean
if (singletonObject == null && allowEarlyReference) { // 二级缓存中没有, 尝试从三级缓存 singletonFactories(FactoryBean)中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) { // 如果三级缓存中有,那么 就把bean放入二级缓存,并在三级缓存中删除
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);// 放入二级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
由于 此过程视为beanB 设置属性值 –BeanB.beanA , 因为之前beanA 已存在singletonFactories中所以,本次对beanA调用doGetBean() 会获取到beanA,根据源码可以得知 beanA 会被放入到earlySingletonObjects中.
- 运行结果如图:
生成beanB并返回
此过程包括了,initializeBean()->addSingleton() [将BeanB加入到singletonObjects中]->return beanB
生成beanA并返回
执行完populateBean()方法后, 缓存 信息为:
由图知:
beanB 加入到singletonObjects中(DefaultSingletonBeanRegistry.addSingleton()),
beanA加入到earlySingletonObjects中(AbstractBeanFactory.getSingleton(beanName))
- doCreateBean() 代码片段:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 3. 加入singletonFactories
// Eagerly cache singletons to be able to resolve circular references(循环依赖)
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));// 标记是否可以处理循环依赖的变量
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}//添加单例工厂, 已创建 单例bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//3. 初始化单例bean
//3. Initialize the bean instance.
Object exposedObject = bean;
try {// 赋值 bean 1. ibp.postProcessAfterInstantiation(); 2. ibp.postProcessProperties()
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);//1. postProcessBeforeInit...();-> init() ;-> afterinit..()
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// 处理循环依赖
if (earlySingletonExposure) {//尝试从二级缓存earlySingletonObjects中获取bean,找不到,就返回exposedObject
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) { //// 取二级缓存中的值为返回值
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// 4. 注册销毁方法
// 4. Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
}
return exposedObject;
}
根据图示:本段内容重点是 getSingleton(beanName,false), 阅读源码后,会发现,getSingleton(beanName,false) 的情况下,只会在singletonObjects 以及earlySingletonObjects 中根据beanname 找目标对象-beanA.
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);// 从单例bean集合中获取(缓存)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 判断当前bean是否正在创建
synchronized (this.singletonObjects) {// 如果一级缓存没有,并且当前bean正在被创建
singletonObject = this.earlySingletonObjects.get(beanName);// 尝试从二级缓存 earlySingletonObject中获取bean
if (singletonObject == null && allowEarlyReference) { // 二级缓存中没有, 尝试从三级缓存 singletonFactories(FactoryBean)中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) { // 如果三级缓存中有,那么 就把bean放入二级缓存,并在三级缓存中删除
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);// 放入二级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
- 根据populateBean 结果图来看:
beanA在earlySingletonObjects中, 所以可以找到.
返回结果只BeanA.
将BeanA 加入到缓存singletonObjects中, 结束.
一个问题
Spring 为什么需要三级缓存? 而不是两级缓存?
通过本篇文章得知, Spring 在处理循环依赖时 ,只是用到了 singletonFactories, earlySingletonObjects 这两个缓存, 而singletonObjects 只是用来存放最终结果,显然这部分并未用到. 所以**,Spring 在处理循环依赖时, 真正工作的只有两个缓存**,singletonObjects 用处是用来存储已处理过, 初始化, AOP代理后(有AOP代理)的单例对象.
建议阅读: here
总结:
Spring 为解决循环依赖采用了三级缓存的策略, 分别是 singletonObjects, earlySingletonObjects, 以及singletonFactories,
整体流程的简单描述:
- 实例化beanA, 并beanA 先放入 singletonFactories中
- BeanA 属性赋值, 即设置 BeanA.beanB
- 实例化beanB, 放入singletonFactories中
- BeanB属性赋值, 即设置BeanB.beanA
- 通过singletonFactories 获取beanA , 将beanA 加入到 earlySingletonObjects 中
- 初始化beanB, 尝试通过二级缓存获取beanB (失败),
- 返回beanB, 并将beanB加入到一级缓存–singletonObjects, 清空其他缓存中的beanB对象.
- 初始化beanA ,尝试通过二级缓存获取beanA (成功)
- 返回beanA ,将beanA 加入一级缓存, 并清空其他缓存中的beanA对象.
参考:
- https://mp.weixin.qq.com/s?__biz=MzAxNjM2MTk0Ng==&mid=2247490824&idx=1&sn=9c35fd9d593adf8fdb2cfaf5e07d7344&scene=21#wechat_redirect
- https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans