在Spring框架中,处理循环依赖一直是一个备受关注的话题。这是因为Spring源代码中为了解决循环依赖问题,进行了大量的处理和优化。同时,循环依赖也是Spring高级面试中的必考问题,回答得好可以成为面试中的必杀技。因此,本文旨在为大家提供深入了解Spring的循环依赖及其解决方案的资料,让读者能够在日后的面试中更有把握地回答相关问题!
一、什么是循环依赖
循环依赖其实就是循环引用,也就是一个或多个以上的对象互相持有对方,最终形成闭环,形成一个无限循环的依赖关系。比如 A依赖于A本身(左图),A依赖于B,B也依赖与A(中),A依赖B,B依赖C,C又依赖A(右图)。
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情,后面我们都以A和B的相互循环依赖进行举例:
A a = new A();
B b = new B();
a.b = b;
b.a = a;
然而,Spring的循环依赖通常被单独拎出来谈论,也经常在面试中被提及。这是因为 Spring 中对象的创建和管理是由 IOC 控制的,一个对象的创建不仅仅是简单地调用 new,而是经过了一系列 Bean 的生命周期。因此,循环依赖问题也就会随之而来。当然,Spring 中存在许多场景会导致循环依赖,有些场景 Spring 能够解决,而有些场景则需要程序员手动解决。
要深刻理解 Spring 中的循环依赖问题,首先需要理解 Spring 中 Bean 的生命周期。
二、Bean的生命周期
Spring Bean的生命周期来说,可以分为四个主要阶段:实例化、属性复制、初始化、销毁,其中经过初始化以后这个bean就被创建完成可以供使用了。
具体的步骤:
- 实例化:实例化一个 Bean 对象
- 属性赋值:为 Bean 设置相关属性和依赖注入
- 初始化:初始化的阶段的步骤比较多,5和6 步是进行真正的初始化,而第 3和4 步为在初始化前执行,第 7 步在初始化后执行,如果原始对象中的某个方法被 AOP 了,那么这一步需要根据原始对象生成一个代理对象,最终生成的代理对象放入单例池,Bean 就可以被使用了。
- 销毁:第 8~10 步,第 8 步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第 9、10 步真正销毁 Bean 时再执行相应的方法
我们可以发现,在第2步中,Spring 需要给对象中的属性进行依赖注入,那么这个注入过程是怎样的?
还是以A和B两个类相互依赖来举例子,A 类中存在一个 B 类的 b 属性,所以当 A 类生成了一个原始对象之后,就需要去给 b 属性去赋值(依赖注入),此时就会根据 b 属性的类型和属性名去 BeanFactory 中去获取 B 类所对应的单例bean。如果此时 BeanFactory 中存在 B 对应的 Bean,那么直接拿来赋值给 b 属性就好了;但是如果此时 BeanFactory 中不存在 B 对应的 Bean,则需要生成一个 B 对应的 Bean,然后赋值给 b属性。
问题的关键点就在于第二种情况,此时 B 类在 BeanFactory 中还没有生成对应的 Bean,那么就需要去生成,就会经过 B 的 Bean 的生命周期。那么在创建 B 类的 Bean 的过程中,如果 B 类中还存在一个 A 类的 a 属性,那么在创建 B 的 Bean 的过程中就需要 A 类对应的 Bean。但是,B 类 Bean创建完成的条件是 A 类 Bean 在创建过程中进行依赖注入,所以这里就出现了循环依赖。
在Spring中,是通过三级缓存来解决循环依赖问题的,那么什么是三级缓存?
三、三级缓存
Spring的三级缓存是指在使用Spring框架进行Bean的创建和管理时,Spring在其内部维护了三级缓存,用于提高Bean的创建和获取效率。这三级缓存分别是singletonObjects、earlySingletonObjects和singletonFactories:singletonObjects用于缓存完全初始化后的单例Bean实例,earlySingletonObjects用于缓存尚未完全初始化的单例Bean实例,而singletonFactories则用于缓存Bean工厂对象,即可以生成Bean实例的工厂方法。
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
一级缓存
singletonObjects:key -> beanName,value -> 完整bean对象
一级缓存的是经历了完整的Bean生命周期的Bean。
二级缓存
earlySingletonObjects:key -> beanName,value -> 不完整(属性信息未填充完毕)bean对象
二级缓存用于存放未完成Bean生命周期的半成品Bean。在出现循环依赖时,这些Bean已经被提前放入二级缓存。如果需要进行AOP处理,则其代理对象也已经被放入缓存。
三级缓存
singletonFactories:key -> beanName,value -> bean工厂对象
缓存的是ObjectFactory , 也就是一个Lambda表达式 , 这就一个方法getObject,返回bean对象或bean代理对象,用于解决被代理增强的循环依赖
四、循环依赖的解决(源码分析)
我们已经知道,问题的关键在于A在创建的时候需要将B注入到A中,而注入B需要先创建B,创建B的时候发现需要将A注入到B中,产生了先有鸡还有现有蛋的问题。Spring解决这个问题的思想就是在实例化过程中,提前办成品bean放入缓存,在依赖注入的时候允许将半成品进行注入:
实例化A -> a的半成品写入缓存 -> 属性注入B -> B还没有实例化,需要先进行实例化B -> 实例化B -> 没有A,但是有A的半成品 -> 注入A的半成品 -> 实例化B成功 -> 实例化A成功
上面就是核心思想,下面将会结合源码进行具体分析其中的原理。
4.1 普通循环依赖
还是使用之前的例子来举例:
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
A和B进行初始化的过程大致如下:
- 使用getBean(A.class),首先获取容器内的单例A(若beanA不存在,就会走A的创建流程,有的话会直接从缓存里面获取),显然初次获取beanA是不存在的,因此会去创建A
- 实例化bean A,并将实例化后的A放入到三级缓存中,此时beanA.b == null
- 对A的属性进行依赖注入:@Autowired依赖注入beanB(此时需要去容器内获取beanB),通过getBean(B)去容器内找B,如果能找到B的实例的话就可以直接注入。但此时B在容器内或者缓存内不存在,因此需要创建B的bean。
- 实例化bean B,并将实例化后的A放入到二级缓存中,此时beanB.A == null
- 对B属性注入:@Autowired依赖注入beanA(此时需要去容器内获取beanA),这时会调用getBean(A)去容器内找到beanA。一级缓存不存在A,在二级缓存中也不存在A,三级缓存中存在A的ObjectFactory,此时会调用getEarlyBeanReference()方法得到A的实例化后的结果,将这个半成品bean放入到二级缓存中,同时将三级缓存中A的ObjectFactory删除掉。
- 将二级缓存中的半成品A注入到B中,B完成后续的初始化过程,最终放入到一级缓存中。
- A也在缓存中可以拿到了B的bean,将B注入到A中。
- A和B都创建完成。
到这里为止有两个问题需要讨论一下,包括我可能存在疑问:
在上面的第6步中,将一个没有经过初始化的A类型对象提前注入B中不会有问题吗?不应该注入一个完整的A吗?
这样做是不会出问题的,虽然给B注入的是一个还未初始化的A对象,也就是半成品A,但是在创建A的流程中一直使用的是注入到B中的A对象的引用,之后会根据这个引用对A进行初始化,通过这个引用最后获取到的还是成品的A,所以这是没有问题的。
为什么三级缓存中存放的是ObjectFactory而不是bean呢?直接将半成品的bean放入到缓存中不可以么?为什么要对此一举再使用三级缓存存放ObjectFactory呢?
这个其实涉及到了下一个要讨论的问题,主要是因为三级缓存实际上跟Spring中的AOP相关,我们继续往下看吧。
4.2 有AOP的循环依赖
到目前为止,发现似乎不需要三级缓存,直接使用二级缓存貌似也能解决问题:
- 实例化A,将实例化后的半成品放入到二级缓存
- 实例化B,从缓存中获取半成品A,完成依赖注入
- 初始化B,得到B的bean
- 将B注入A,完成A和B的初始化
还记得第二部分Bean的生命周期吗,在生命周期的初始化过程中,如果有AOP的话需要执行相应的方法。还是A和B两个类相互依赖的场景,但是A类中多了一层AOP,也就是A类的原始对象赋值给B时 , 进行了AOP , 那么A进行AOP之后,它的真实对象是代理对象 , 那么B中A的值是原始对象的值 ,那么就会产生B的A值和A的实际值不符的问题,Spring解决的办法就是使用三级缓存。如果我们使用了三级缓存,就可以实现对A的提前AOP,将B真实依赖的A注入到B中。
那改变Bean的生命周期可以么?先AOP再放入缓存中呢?
如果这么做了,就把AOP中创建代理对象的时机提前了,不管是否发生循环依赖,都在doCreateBean方法中完成了AOP的代理。不仅没有必要,而且违背了Spring在结合AOP跟Bean的生命周期的设计。
五、哪种情况的循环可以解决
循环依赖问题在Spring中主要有三种情况:
- 通过构造方法进行依赖注入时产生的循环依赖问题。
- 通过setter方法进行依赖注入且是在多例模式下产生的循环依赖问题。
- 通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。
在Spring中,只有第三种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。
多例模式不可以:只有单例bean才有支持循环依赖的可能,非单例的bean不支持循环依赖,会陷入死循环。
构造方法注入不可以:如果主bean对象通过构造函数方式注入所依赖的bean对象,则无论所依赖的bean对象通过何种方式注入主bean,都无法解决循环依赖问题,程序无法启动。主要原因是主bean对象通过构造函数注入所依赖bean对象时,无法创建该所依赖的bean对象,获取该所依赖bean对象的引用。
@Async导致无法支持循环依赖:@Async 标记的类是通过 AbstractAdvisingBeanPostProcessor 来生成代理的,AbstractAdvisingBeanPostProcessor 没有实现 SmartInstantiationAwareBeanPostProcessor
六、源码分析
现在进入源码分析部分。下面按照方法的调用顺序,依次来看一下循环依赖相关的代码。
获取单例Bean的源码
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
...
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
protected boolean isActuallyInCreation(String beanName) {
return isSingletonCurrentlyInCreation(beanName);
}
...
}
doGetBean源码
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
...
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
...
// Eagerly check singleton cache for manually registered singletons.
// 先去获取一次,如果不为null,此处就会走缓存了
Object sharedInstance = getSingleton(beanName);
...
// 如果不是只检查类型,那就标记这个Bean被创建了,添加到缓存里 也就是所谓的 当前创建Bean池
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
...
// Create bean instance.
if (mbd.isSingleton()) {
// 这个getSingleton方法不是SingletonBeanRegistry的接口方法 属于实现类DefaultSingletonBeanRegistry的一个public重载方法
// 它的特点是在执行singletonFactory.getObject();前后会执行beforeSingletonCreation(beanName);和afterSingletonCreation(beanName);
// 也就是保证这个Bean在创建过程中,放入正在创建的缓存池里 可以看到它实际创建bean调用的是我们的createBean方法
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
...
}
// 抽象方法createBean所在地 这个接口方法是属于抽象父类AbstractBeanFactory的 实现在这个抽象类里
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
...
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
...
// 创建Bean对象,并且将对象包裹在BeanWrapper 中
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 再从Wrapper中把Bean原始对象(非代理) 这个时候这个Bean就有地址值了,就能被引用了
// 注意:此处是原始对象,这点非常的重要
final Object bean = instanceWrapper.getWrappedInstance();
...
// earlySingletonExposure 用于表示是否”提前暴露“原始对象的引用,用于解决循环依赖。
// 对于单例Bean,该变量一般为 true 但你也可以通过属性allowCircularReferences = false来关闭循环引用
// isSingletonCurrentlyInCreation(beanName) 表示当前bean必须在创建中才行
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");
}
// 上面讲过调用此方法放进一个ObjectFactory,二级缓存会对应删除的
// getEarlyBeanReference的作用:调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()这个方法 否则啥都不做
// 也就是给调用者个机会,自己去实现暴露这个bean的应用的逻辑
// 比如在getEarlyBeanReference()里可以实现AOP的逻辑 参考自动代理创建器AbstractAutoProxyCreator 实现了这个方法来创建代理对象
// 若不需要执行AOP的逻辑,直接返回Bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean; //exposedObject 是最终返回的对象
...
// 填充属于,解决@Autowired依赖
populateBean(beanName, mbd, instanceWrapper);
// 执行初始化回调方法们
exposedObject = initializeBean(beanName, exposedObject, mbd);
// earlySingletonExposure:如果你的bean允许被早期暴露出去 也就是说可以被循环引用 那这里就会进行检查
// 此段代码非常重要,但大多数人都忽略了它
if (earlySingletonExposure) {
// 此时一级缓存肯定还没数据,但是呢此时候二级缓存earlySingletonObjects也没数据
//注意,注意:第二参数为false 表示不会再去三级缓存里查了
// 此处非常巧妙的一点:因为上面各式各样的实例化、初始化的后置处理器都执行了,如果你在上面执行了这一句
// ((ConfigurableListableBeanFactory)this.beanFactory).registerSingleton(beanName, bean);
// 那么此处得到的earlySingletonReference 的引用最终会是你手动放进去的Bean最终返回,完美的实现了"偷天换日" 特别适合中间件的设计
// 我们知道,执行完此doCreateBean后执行addSingleton() 其实就是把自己再添加一次 ,再一次强调,完美实现偷天换日
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 这个意思是如果经过了initializeBean()后,exposedObject还是木有变,那就可以大胆放心的返回了
// initializeBean会调用后置处理器,这个时候可以生成一个代理对象,那这个时候它哥俩就不会相等了 走else去判断吧
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// allowRawInjectionDespiteWrapping这个值默认是false
// hasDependentBean:若它有依赖的bean 那就需要继续校验了(若没有依赖的 就放过它)
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 拿到它所依赖的Bean们,下面会遍历一个一个的去看
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// 一个个检查它所以Bean
// removeSingletonIfCreatedForTypeCheckOnly这个放见下面 在AbstractBeanFactory里面
// 简单的说,它如果判断到该dependentBean并没有在创建中的了的情况下,那就把它从所有缓存中移除, 并且返回true
// 否则(比如确实在创建中) 那就返回false 进入我们的if里面~ 表示所谓的真正依赖
//(解释:就是真的需要依赖它先实例化,才能实例化自己的依赖)
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// 若存在真正依赖,那就报错(不要等到内存移除你才报错,那是非常不友好的)
// 这个异常是BeanCurrentlyInCreationException,报错日志也稍微留意一下,方便定位错误
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.");
}
}
}
}
return exposedObject;
}
// 虽然是remove方法 但是它的返回值也非常重要
// 该方法唯一调用的地方就是循环依赖的最后检查处
protected boolean removeSingletonIfCreatedForTypeCheckOnly(String beanName) {
// 如果这个bean不在创建中 比如是ForTypeCheckOnly的 那就移除掉
if (!this.alreadyCreated.contains(beanName)) {
removeSingleton(beanName);
return true;
}
else {
return false;
}
}
}
protected <T> T doGetBean(...){
...
// 标记beanName a是已经创建过至少一次的,它会一直存留在缓存里不会被移除(除非抛出了异常)
// 参见缓存Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256))
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
// 此时a不存在任何一级缓存中,且不是在创建中 所以此处返回null
// 此处若不为null,然后从缓存里拿就可以了(主要处理FactoryBean和BeanFactory情况吧)
Object beanInstance = getSingleton(beanName, false);
...
// 这个getSingleton方法非常关键。
//1、标注a正在创建中
//2、调用singletonObject = singletonFactory.getObject();(实际上调用的是createBean()方法) 因此这一步最为关键
//3、此时实例已经创建完成 会把a移除整整创建的缓存中
//4、执行addSingleton()添加进去。(备注:注册bean的接口方法为registerSingleton,它依赖于addSingleton方法)
sharedInstance = getSingleton(beanName, () -> { ... return createBean(beanName, mbd, args); });
}
getSingleton(beanName, singletonFactory)源码
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ....
// 省略异常处理及日志
// ....
// 在单例对象创建前先做一个标记
// 将beanName放入到singletonsCurrentlyInCreation这个集合中
// 标志着这个单例Bean正在创建
// 如果同一个单例Bean多次被创建,这里会抛出异常
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// ...
// 省略catch异常处理
// ...
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到一级缓存singletonObjects中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
doCreateBean源码
protected Object doCreateBean(){
...
// 使用构造器/工厂方法 instanceWrapper是一个BeanWrapper
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 此处bean为"原始Bean" 也就是这里的A实例对象:beanA@1
final Object bean = instanceWrapper.getWrappedInstance();
...
// 是否要提前暴露(允许循环依赖) 现在此处A是被允许的
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
// 允许暴露,就把A绑定在ObjectFactory上,注册到三级缓存`singletonFactories`里面去保存着
// Tips:这里后置处理器的getEarlyBeanReference方法会被促发,自动代理创建器在此处创建代理对象(注意执行时机 为执行三级缓存的时候)
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
...
// exposedObject 为最终返回的对象,此处为原始对象bean也就是beanA@1,下面会有用处
Object exposedObject = bean;
// 给A@1234属性完成赋值,@Autowired在此处起作用
// 因此此处会调用getBean("b"),so 会重复上面步骤创建B类的实例
// 此处我们假设B已经创建好了 为beanB@2
// 需要注意的是在populateBean("b")的时候依赖有beanA,所以此时候调用getBean("a")最终会调用getSingleton("a"),
//此时候上面说到的getEarlyBeanReference方法就会被执行。这也解释为何我们@Autowired是个代理对象,而不是普通对象的根本原因
populateBean(beanName, mbd, instanceWrapper);
// 实例化。这里会执行后置处理器BeanPostProcessor的两个方法
// 此处注意:postProcessAfterInitialization()是有可能返回一个代理对象的,这样exposedObject 就不再是原始对象了 需要特别注意
// 比如处理@Aysnc的AsyncAnnotationBeanPostProcessor它就是在这个时间里生成代理对象的(有坑,请小心使用@Aysnc)
exposedObject = initializeBean(beanName, exposedObject, mbd);
... // 至此,相当于beanA@1已经实例化完成、初始化完成(属性也全部赋值了)
// 这一步我把它理解为校验:校验:校验是否有循环引用问题
if (earlySingletonExposure) {
// 注意此处第二个参数传的false,表示不去三级缓存里singletonFactories再去调用一次getObject()方法了
// 上面建讲到了由于B在初始化的时候,会触发A的ObjectFactory.getObject() 所以a此处已经在二级缓存earlySingletonObjects里了
// 因此此处返回A的实例:beanA@1
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 这个等式表示,exposedObject若没有再被代理过,这里就是相等的
// 显然此处我们的a对象的exposedObject它是没有被代理过的 所以if会进去
// 这种情况至此,就全部结束了
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// 继续以A为例,比如方法标注了@Aysnc注解,exposedObject此时候就是一个代理对象,因此就会进到这里来
//hasDependentBean(beanName)是肯定为true,因为getDependentBeans(beanName)得到的是["b"]这个依赖
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// beanA@1依赖的是["b"],所以此处去检查b
// 如果最终存在实际依赖的bean:actualDependentBeans不为空 那就抛出异常 证明循环引用了
for (String dependentBean : dependentBeans) {
// 这个判断原则是:如果此时候b并还没有创建好,this.alreadyCreated.contains(beanName)=true表示此bean已经被创建过,就返回false
// 若该bean没有在alreadyCreated缓存里,就是说没被创建过(其实只有CreatedForTypeCheckOnly才会是此仓库)
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.");
}
}
}
}
}
七、常见面试题
7.1 Spring是如何解决循环依赖的
Spring使用了三级缓存来解决循环依赖。其中一级缓存存放完整bean对象,二级缓存存放半成品bean,三级缓存为对象工厂。
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂并添加到三级缓存中。如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,通过这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束。
7.2 为什么需要三级缓存,二级缓存可以么?
不可以,主要为了解决AOP生产代理对象问题的。如果存在代理,三级没有问题,二级就不行了。因为三级缓存中放的是⽣成具体对象的匿名内部类,获取 Object 的时候,它可以⽣成代理对象,也可以返回普通对象。使⽤三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对象。
如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则。
7.3 Spring能自动解决全部的循环依赖情况么?
- 只有单例bean才有支持循环依赖的可能,非单例的bean不会出现循环依赖。
- 如果存在循环依赖,且都是通过构造函数依赖的,这种情况下的循环依赖是无法解决的。
参考文献:文章来源:https://www.toymoban.com/news/detail-417409.html
Spring 轻度解析之循环依赖源码解析
面试必杀技,讲一讲Spring中的循环依赖
Spring中的循环依赖
聊透Spring循环依赖文章来源地址https://www.toymoban.com/news/detail-417409.html
到了这里,关于万字长文带你吃透Spring是怎样解决循环依赖的的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!