Spring循环依赖冤冤相报何时了
Posted 洪宏鸿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring循环依赖冤冤相报何时了相关的知识,希望对你有一定的参考价值。
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中。
Spring Boot源码:循环依赖
循环依赖
以及
spring是如何解决循环依赖的
循环依赖
通俗来说
就是beanA中依赖了beanB,beanB中也依赖了beanA。
spring是支持循环依赖的,但是默认只支持单例的循环依赖,如果bean中依赖了原型bean,则需要加上lookup方法。
继续之前的项目,改造了People,User类:
@Component public class People { @Autowired private User user; public People(){ System.out.println("create People"); } }
@Component public class User { @Autowired private People people; public User(){ System.out.println("create User"); } }
People--(依赖)-->User--(依赖)-->People--(依赖)-->User.......,这就形成了循环依赖。
继续进入上篇的preInstantiateSingletons()方法:
进行条件断点:
进入上篇说的getSingleTon()方法:
singletonObjects是spring的单例池,从其中没有拿到bean,singletonObject为null,且isSingletonCurrentlyInCreation()为false。
public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); }
其实这个方法就是去判断该map(singletonCurrentlyInCreation)是否包含该bean?(这里是people)。
这个方法通俗意思就是 “该bean是否在创建过程中?”
先不管这个意思,继续往下走。
spring创建对象前还会做很多判断,例如是否为原型,是否抽象?
这里的getSingleton(String beanName, ObjectFactory<?> singletonFactory)是重载,与之前的getSingleton(String beanName, boolean allowEarlyReference)不同。
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) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean \'" + beanName + "\'"); } beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
此方法把 “people”放入singletonsCurrentlyInCreation中,那么isSingletonCurrentlyInCreation("people")这时为true。
进入lambda表达式 createBean()
create People Object ----注入依赖发现依赖User--->create User Object---发现依赖People---create People Object---发现isSingletonCurrentlyInCreation(beanName)为true,表示正在创建People:
然后才会执行里面的代码:
allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象,也就是执行if中的方法。
我们看一下singletonObjects(一级缓存),earlySingletonObjects(二级缓存),singletonFactories(三级缓存)。
AbstractAutowireCapableBeanFactory#doCreateBean()中:
这时候存进去的并不是完整的bean:
但是可以供User依赖注入了,这也就是spring对循环依赖的解决。
以上是关于Spring循环依赖冤冤相报何时了的主要内容,如果未能解决你的问题,请参考以下文章