彻底理解Spring如何解决循环依赖
Spring bean生命周期
可以简化为以下5步。
1、构建BeanDefinition
2、实例化 Instantiation
3、属性赋值 Populate
4、初始化 Initialization(BeanPostprocessor -> Aware,init)
5、销毁 Destruction
Spring 三级缓存作用
一级缓存
/** Cache of singleton objects: bean name to bean instance. */ Map<String, Object> singletonObjects;
用来保存实例化、初始化都完成的bean对象。
二级缓存
/** Cache of early singleton objects: bean name to bean instance. */ Map<String, Object> earlySingletonObjects ;
用来保存实例化完成,但是未初始化完成的对象(这个对象不一定是原始对象,也有可能是经过AOP生成的代理对象)。
三级缓存
/** Cache of singleton factories: bean name to ObjectFactory. */ Map<String, ObjectFactory<?>> singletonFactories;
用来保存一个对象工厂(ObjectFactory),提供一个匿名内部类,用于创建二级缓存中的对象。
三级缓存中提到的ObjectFactory即 () -> getEarlyBeanReference(beanName,mbd,bean),其中bean就是原始对象。
其中getEarlyBeanReference 方法是 SmartInstantiationAwareBeanPostProcessor接口中定义的,AbstractAutoProxyCreator(Spring AOP proxy creator)实现了该方法。
Spring三级缓存实现
获取beanName:A
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:true)
分别按照一级缓存、二级缓存、三级缓存顺序加载。如果存在循环依赖(比如beanName:B依赖beanName:A),而且三级缓存中存在beanName:A的引用,则从三级缓存中拿到beanName:A对应的提早曝光的对象(可能是原始对象,也可能是代理对象)并放入二级缓存。比如又有beanName:C依赖beanName:A,会直接从二级缓存中获取到。
bean创建和初始化完成
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, ObjectFactory:lamda表达,调用AbstractBeanFactory#createBean(beanName:A))
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton(beanName:A,singletonObject:A)
直接添加到一级缓存
bean创建完成之后
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName:A, RootBeanDefinition:mbd, Object[]:args) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(beanName:A, RootBeanDefinition:mbd, Object[]:args) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory(beanName:A, () -> getEarlyBeanReference(beanName:A, mbd, bean:A)) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean //属性填充 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean //初始化
将ObjectFactory对象放入三级缓存,beanName:A -> ObjectFactory( () -> getEarlyBeanReference(beanName:A, mbd, bean:A) )
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); if (earlySingletonExposure) {
//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:false) Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { } } } }
一个简单的A、B互相依赖循环依赖场景
@Async注解循环依赖报错
@Transactional使用的是自动代理创建器AbstractAutoProxyCreator,它实现了getEarlyBeanReference()方法从而很好的对循环依赖提供了支持。
@Async的代理创建使用的是AsyncAnnotationBeanPostProcessor单独的后置处理器实现的,它只在一处postProcessAfterInitialization()实现了对代理对象的创建,因此若出现它被循环依赖了,就会报BeanCurrentlyInCreationException。
protected Object doCreateBean( ... ){ ... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } ... Object exposedObject = bean; /** *假如A实例有方法注有@Async注解,A实例依赖B实例,B实例依赖A实例。创建A实例时先走到populateBean方法,然后开始填充属性B实例,B实例也会走到populateBean方法,然后从三级缓存中通过A流程中的getEarlyBeanReference()方法,从而拿到A的早期引用*。执行A的getEarlyBeanReference()方法的时候,会执行自动代理创建器,这里最终得到是可能是原始A对象,也可能是代理后的A对象(注意哦,A实例的@Async注解这里还没有被处理呢)。exposedObject此时指向的是原始A实例。 */ populateBean(beanName, mbd, instanceWrapper); /** *标注有@Async的A实例的代理对象在此处会被生成, 参照类:AsyncAnnotationBeanPostProcessor。执行完之后,exposedObject指向的是个代理对象而非原始A实例了。 */ exposedObject = initializeBean(beanName, exposedObject, mbd); ... // 这里是报错的重点。 if (earlySingletonExposure) { /** *因为A被B循环依赖进去了,所以此时A是被放进了二级缓存的,所以此处earlySingletonReference指向的是通过创建A实例流程中的getEarlyBeanReference()返回的A实例(再强调用一下,可能是原始对象,也可能是代理对象)。 *说到这里,什么情况下earlySingletonReference==null?也就是getSingleton(beanName:A, false)==null,只有当A实例没有牵涉到循环依赖的时候(即不存在A依赖B且B依赖A的场景;单独存在B依赖A是没有问题,A的三级缓存根本不会执行,所以二级缓存就不会有值,A创建并初始化完成之后直接放到了一级缓存)。 */ Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //这里exposedObject指向的是被@Aysnc代理过的对象,而bean是原始对象,所以此处不相等,走else逻辑。 if (exposedObject == bean) { exposedObject = earlySingletonReference; } /** *allowRawInjectionDespiteWrapping 标注是否允许此Bean的原始类型被注入到其它Bean里面,即使自己最终会被包装(代理)。 *默认是false表示不允许,如果改为true表示允许,就不会报错啦。 *另外dependentBeanMap是记录着每个Bean它所依赖的Bean的Map。 */ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { //因为A依赖于B,所以此处拿到了B实例的beanName String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); /** *B实例经过removeSingletonIfCreatedForTypeCheckOnly最终返返回false 因为alreadyCreated里面已经有它了表示B已经完全创建完成了。 *既然B实例已经创建完成了,通过创建A实例流程中的getEarlyBeanReference()返回的A实例已经注入到了B实例中,此时B实例注入的和exposedObject指向的不是同一个A实例,那肯定就有问题了。 */ for (String dependentBean : dependentBeans) { 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."); } } } } ... }
具体原因分析参考:https://blog.csdn.net/f641385712/article/details/92797058
总结
1、Spring 解决循环依赖有两个前提条件:不全是构造器方式的循环依赖,必须是单例。
2、如果没有出现循环依赖,第三级缓存(singletonFactories)将不会使用到,对象会按照Spring创建bean的生命周期流程,最后将bean直接放到第一级缓存(singletonObjects)中。
3、一定要三级缓存嘛,二级缓存不能解决循环依赖?不能,主要是为了生成代理对象。
因为三级缓存中放的是生成具体对象的匿名内部类(ObjectFactory),它可能生成代理对象,也可能是普通的实例对象。使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor
去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,无法保证程多线程环境下获取到bean对象一致性。
本文来自博客园,作者:hjzqyx,转载请注明原文链接:https://www.cnblogs.com/hujunzheng/p/14094804.html