前面说到对象的创建,那么在创建的过程中Spring是怎么又是如何解决循环依赖的呢。前面提到有个三级缓存。就是利用这个来解决循环依赖。打个比方说实例化A的时候,先将A创建(早期对象)放入一个池子中。这个时候虽然属性没有赋值,但是容器已经能认识这个是A对象,只是属性全是null而已。在populateBean方法中对属性赋值的时候,发现A依赖了B,那么就先去创建B了,又走一遍bean的创建过程(创建B)。同样也会把B的早期对象放入缓存中。当B又走到populateBean方法的时候,发现依赖了A,好吧,我们又去创建A呗,但是这个时候去创建A,发现我们在缓存能找到A(早期对象)了。就可以把B的A属性赋值了,这个时候B就初始化完成了。现在回到A调用的populateBean方法中。返回的就是B对象了,对A的B属性进行赋值就可以了。流程如下:
这就是Spring IOC如何解决循环依赖的原理,但是IOC无法解决两种循环依赖,一种是非单例对象的,因为非单例对象不会放入缓存的。每次都是需要创建。二是通过构造器注入,也无法解决。从上面的流程可以看出,调用构造器创建实例是在createBeanInstance方法,而解决循环依赖是在populateBean这个方法中,执行顺序也决定了无法解决该种循环依赖。对于这种,如果喜欢使用lombok的@RequiredArgsConstructor注解的小伙伴就需要注意了,这个注解会生成一个带所有属性的构造方法。通过idea直接点开对应的class文件就可以看不看这个构造方法了。
问题:解决循环依赖一定要三级缓存嘛?
关键代码位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
首先我们的一级缓存是单例缓存池(singletonObjects),二级缓存是早期对象(earlySingletonObjects),三级缓存是一个包裹对象ObjectFactory(registeredSingletons),通过getObject获取到早期对象。
从上面的流程来看,实际上二级缓存已经可以解决循环依赖了,那么为什么Spring还要包裹出来一个三级缓存呢?
从Spring Ioc(3)---bean的实例化中的源码分析我们可以看到调用三级缓存中的getObject方法实际上是调用的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference这个方法。
那么我就看看这个方法里面干了些什么事情:
执行SmartInstantiationAwareBeanPostProcessor这个类型处理器的getEarlyBeanReference方法。
断点调试,发现Spring原生中实现了这个接口的类org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter,实际上什么事情都没有干,直接返回了,
但是如果是AOP要切的对象,会在这里生成代理对象返回,AOP流程:Spring aop(1)--- 寻找切面和代理对象执行流程源码分析
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference。
所以说第三级缓存其实是为了解决代理对象之间的循环依赖。
来源:https://www.cnblogs.com/nijunyang/p/12365048.html
自己理解的案例:
一级缓存:单例池,成型的SpringBean的缓存
二级缓存:早期池
三级缓存:singletonFactorys:为了提前暴露自己
Class A {
private B b;
}
Class B {
private A a;
}
当初始化A的时候,A在对象实例化之后立马放入三级缓存(提前暴露自己);当A创建的过程时发现依赖B,则进行B的初始化,同样B在对象实例化之后立马放入三级缓存(提前暴露自己),同时创建过程中发现依赖A,那么去三级缓存使用未成形的Bean A,并将A升级放到二级缓存中,然后B继续初始化并完成初始化,将B放入到一级缓存,并将二级和三级缓存中移除自己,然后代码返回到A初始化过程,这时候将完整的B复制给A的属性上,A继续初始化并完成初始化,将A放入到一级缓存,并将二级和三级缓存中移除自己。这就是解决循环依赖的过程!
指的注意的是: 只有是单例并且是不通过构造器注入的依赖Bean才能这么解决循环依赖问题。
这就是Spring IOC如何解决循环依赖的原理,但是IOC无法解决两种循环依赖:
一种是非单例对象的,因为非单例对象不会放入缓存的。每次都是需要创建。
二是通过构造器注入,也无法解决。从上面的流程可以看出,调用构造器创建实例是在createBeanInstance方法,而解决循环依赖是在populateBean这个方法中,执行顺序也决定了无法解决该种循环依赖。