细致又通透:spring如何解决循环依赖

细致又通透:spring如何解决循环依赖,第1张

背景

大致简述一下spring里bean的生命周期:

spring扫描class,获取beanDefinition对象

根据beanDefinition对象生成bean

根据class推断构造方法,反射生成一个原始对象

为这个原始对象填充属性(依赖注入)

如果存在aop则生成一个代理对象

把最终生成的对象放在singletonObjects单例池中
步入正题

什么是循环依赖?

比如 spring在加载A,在构造完A类后为A类填充属性,发现A类中依赖了B类,spring再去加载B类(前提是B类没有被加载过),构造完B类后为B类填充属性,发现B类里面依赖了A类,但此时A类并没有完全经过整个生命周期,不在singletonObjects单例池中,那么Spring无法拿到A填充给B类,这时就会导致循环依赖的问题,类似死锁。

解决

大多数人可能会想到给加一个缓存作为中间人不就行了。好那我们假如这个中间人为earlySingletonObjects单例池:

创建A

将A放入earlySingletonObjects中

A依赖注入,需要注入B

创建B

将B放入earlySingletonObjects中

B依赖注入,需要注入A

判断earlySingletonObjects中是否有A

存在,B对A依赖注入完成

将B放入singletonObjects

A依赖注入完成

将A放入singletonObjects

这样一看好像确实没有什么问题,但其实别忘了Spring的两大特性IOC和AOP

如果A中存在aop,那么A最后在依赖注入完成后往singletonObjects放入的是一个代理对象,但是从上面流程我们看到B依赖的A是earlySingletonObjects中的A,而存放在earlySingletonObjects中的A还没有经过aop生成代理对象,是一个原始对象。那么这样实际上已经违反了spring的IOC控制反转的原则,同时存在了两个不同的A对象。

现在来看看Spring是怎么做的呢

Spring解决循环依赖

三级缓存

  • singletonObjects :缓存经过完整生命周期的bean
  • earlySingletonObjects :缓存未经过完整生命周期的bean
  • singletonFactories :缓存了一个ObjectFactory,一个lambda表达式

其实还有一个缓存 earlyProxyReferences 用来记录某个原始对象是否进行过AOP

前面singletonObjects和earlySingletonObjects我们都知道是干嘛的,那么singletonFactories是存放什么呢,会存放一个lambda表达式

() -> getEarlyBeanReference(beanName, mbd, bean)

getEarlyBeanReference这个方法我们看下源码:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp =
                        (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

他会实现SmartInstantiationAwareBeanPostProcessor 方法中的getEarlyBeanReference()方法:

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	this.earlyProxyReferences.put(cacheKey, bean);
	return wrapIfNecessary(bean, beanName, cacheKey);
}

目前整个spring里面只有AbstractAutoProxyCreator去实现了getEarlyBeanReference这个方法。我们再来看下:

创建A

为A对象生成一个objectFactory放入singletonFactories中

A依赖注入,需要注入B

创建B

为B对象生成一个objectFactory放入singletonFactories中

B依赖注入,需要注入A

先从earlySingletonObjects中查找是否存在A,存在则直接注入完成

不存在,则通过三级缓存池singletonFactories获取A的objectFactory生成对象,
如果A存在AOP则提前对A进行AOP,将生成的结果放入二级缓存池earlySingletonObjects中,
再去除singletonFactories中A的objectFactory

B对A依赖注入完成,B完成整体bean的生命周期,放入一级缓存池singletonObjects中

A对B依赖注入完成,A完成整体bean的生命周期,放入一级缓存池singletonObjects中
结语

以上流程是大致的流程,真正的好代码是需要亲自去看去理解的。

当然Spring解决循环依赖的方法可以解决大多数循环依赖,但是还是会存在依赖深度较复杂,Spring无法解决,这个时候可以在导致循环依赖的地方加上@Lazy注解,对改依赖进行懒加载,只有使用到改依赖时才会进行加载。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/924124.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存