Spring循环依赖 🚎
- 什么是循环依赖?
- 那么循环依赖是个问题吗?
- 单例setter, spring是如何解决的
- 为什么需要三级缓存
- 单单只有两级缓存行不行
- 还得是你三级缓存
- 三级缓存解决依赖循环全解
梦想不会逃跑,会逃跑的永远都是自己
什么是循环依赖?
// A依赖了B
class A{
public B b;
}
// B依赖了A
class B{
public A a;
}
那么循环依赖是个问题吗?
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。
但是spring中bean是有自己的一套生命周期, 正因为Bean的生命周期所以才会出现循环依赖问题。当然,在Spring中,出现循环依赖的场景很多,有的场景Spring自动帮我们解决了(单例setter),而有的场景(构造器)则需要程序员来解决
如果对Bean的生命周期不甚了解, 可以先了解一下, 不然往下看怕是有点难受
单例setter, spring是如何解决的
下面说一下单例setter, spring是如何解决的
spring中通过三级缓存进行解决.
三级缓存的作用可以大概看一下, 大概记得有这么三个缓存先, 不急不急
三级缓存是通用的叫法。
-
一级缓存为:singletonObjects
- singletonObjects中缓存的是已经经历了完整生命周期的bean对象
-
二级缓存为:earlySingletonObjects
- earlySingletonObjects比singletonObjects多了一个early,表示缓存的是早期的bean对象。早期是什么意思?表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects
-
三级缓存为:singletonFactories
- singletonFactories中缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂
为什么需要三级缓存
原本声明周期中就有一个缓存 singletonObjects 存放我们已经初始化后的对象, 有aop对象, 也有普通对象.
既然
单单只有两级缓存行不行
我先说一下我的思路, 有两个类A和B
首先我用一个缓存, 就叫它 singletonFactories 吧
缓存我们实例化后填充属性之前的原始对象
A 放进 singletonFactories 了
发现依赖 B, 实例化 B, 放进 singletonFactories,
好了发现依赖 A, 从 singletonFactories 拿到,
B 继续初始化, 完事
普通的依赖对象没问题, 可AOP呢, 我AOP怎么办
二级缓存表示不服, 我实例化后就直接创建AOP对象
但是有一个问题, 那就是不是所有类都需要AOP呀
这是后置处理器(初始化过程一环)干的活, 怎么让我一个干实例化的干初始化的活
我还要实例化的时候判断是否循环依赖了, 这倒是可以使用一个集合存放正在创建的类来判断
在这里创建AOP倒是有一个好处, 我可以直接拿到原始对象
PS: 动态代理需要用到原始对象, 当然Cglib也需要
既然太靠前了, 那我能不能等到真正等到真正循环依赖的时候就再进行AOP呢?
好了, 二个缓存都搞不定
还得是你三级缓存
AOP, 我三级缓存来救你了
OK, 我现在增加一个缓存, 叫做 earlySingletonObjects, 用来存放还没完全填充属性和初始化好的对象, 通常是普通对象也可以存放AOP对象
好, 继续说说 A 和 B
A 示例化好后, 我把原始对象放到三级缓存中,
A 填充属性, 发现依赖 B,
B 实例化, 发现依赖 A,
分别从一级, 二级, 三级,如果拿到一级/二级就返回,
如果拿到三级, 那我就创建二级对象, 并且放到二级缓存,
有好奇的同学就要问了, 那我二级缓存也能创建 AOP 对象呀
当然可以, 但是这样就创建了多个 AOP 对象,
二级缓存之后, 就算有多个对象依赖 A, 我们也可以拿到同一个 AOP 代理对象,
而不是多个
其实 Spring 第三级缓存里面放的不是原始的实例化好的对象,
而是 ObjectFactory
通过 lambda 表达式进行创建
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
PS: 该方法会去执行SmartInstantiationAwareBeanPostProcessor中的getEarlyBeanReference方法,而这个接口下的实现类中只有两个类实现了这个方法,一个是AbstractAutoProxyCreator(AOP代理),一个是InstantiationAwareBeanPostProcessorAdapter
这样我们拿到 ObjectFactory 就能拿到当初的原始对象进行创建代理对象
AnnotationAwareAspectJAutoProxyCreator的父类就是AbstractAutoProxyCreator
AOP: 谢谢你, 我活下来了
ObjectFactory
使用ObjectFactory的方式不仅可以避免循环依赖问题,还可以为我们提供更多自定义实例化逻辑的机会。例如,我们可以使用ObjectFactory来创建代理对象、注入特定的依赖、在创建Bean实例之前或之后执行一些操作等等。
这里使用 ObjectFactory 而不直接把原始对象放置到三级缓存中, 是让我们可以做一些初始化的动作, 我们可以实现 SmartInstantiationAwareBeanPostProcessor
可以查看我的另一篇文章 spring5.1+SmartInstantiationAwareBeanPostProcessor 解决循环依赖_洪宏鸿的博客-CSDN博客
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
三级缓存解决依赖循环全解
首先,singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始对象 之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持 Lambda表达式:()-> getEarlyBeanReference(beanName, mbd,bean)
上面的Lambda表达式就是一个ObjectFactory,执行该Lambda表达式就会去执行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方法,而这个接口下的实现类中只有两个类实现了这个方法,一个是AbstractAutoProxyCreator,一个是
InstantiationAwareBeanPostProcessorAdapter,它的实现如下:
// InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
// AbstractAutoProxyCreator
@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方法,而该类就是用来进行AOP的。上文提到的
AnnotationAwareAspectJAutoProxyCreator的父类就是AbstractAutoProxyCreator。 那么getEarlyBeanReference方法到底在干什么?
首先得到一个cachekey,cachekey就是beanName。
然后把beanName和bean(这是原始对象)存入earlyProxyReferences中 调用wraplfNecessary进行AOP,得到一个代理对象。
那么,什么时候会调用getEarlyBeanReference方法呢?回到循环依赖的场景中
左边文字:
这个ObjectFactory就是上文说的labmda表达式,中间有getEarlyBeanReference方法,注意存入 singletonFactories时并不会执行lambda表达式,也就是不会执行getEarlyBeanReference方法
右边文字:
从singletonFactories根据beanName得到一个ObjectFactory,然后执行
ObjectFactory,也就是执行getEarlyBeanReference方法,此时会得到一个A原始对象 经过AOP之后的代理对象,然后把该代理对象放入earlySingletonObjects中,注意此时并没有把代理对象放入singletonObjects中,那什么时候放入到singletonObjects中 呢?
我们这个时候得来理解一下earlySingletonObjects的作用,此时,我们只得到了A原始对象的代理对象,这个对象还不完整,因为A原始对象还没有进行属性填充,所以此时不能直接把A的代理对象放入singletonObjects中,所以只能把代理对象放入
earlySingletonObjects,假设现在有其他对象依赖了A,那么则可以从
earlySingletonObjects中得到A原始对象的代理对象了,并且是A的同一个代理对象。
当B创建完了之后,A继续进行生命周期,而A在完成属性注入后,会按照它本身的逻辑去进行AOP,而此时我们知道A原始对象已经经历过了AOP,所以对于A本身而言,不会再去进行AOP了,那么怎么判断一个对象是否经历过了AOP呢?会利用上文提到的earlyProxyReferences,在AbstractAutoProxyCreator的
postProcessAfterlnitialization方法中,会去判断当前beanName是否在
earlyProxyReferences,如果在则表示已经提前进行过AOP了,无需再次进行AOP。
对于A而言,进行了AOP的判断后,以及BeanPostProcessor的执行之后,就需要把A对应的对象放入singletonObjects中了,但是我们知道,应该是要把A的代理对象放入
singletonObjects中,所以此时需要从earlySingletonObjects中得到代理对象,然后入 singletonObjects中。