spring boot循环依赖,spring循环依赖到底怎么解决的
00-1010 1.循环依赖2。循环依赖形成条件(由构造函数注入)3。循环依赖形成条件(由@Aysnc标注的bean生成对象的代理)4。针对以上问题,本文详细阐述了Spring是如何解决循环依赖的。
00-1010顾名思义,多个类中的依赖形成了一个循环,形成了类似死锁的情况,导致springboot无法在启动时为我们创建Bean。一般来说,beanA依赖于beanB,beanB也依赖于beanA。
Spring支持循环依赖,但是默认情况下,它只支持单实例循环依赖。如果bean依赖于原型bean,您需要添加查找方法。Spring将为我们解决循环依赖。
@Autowired通过L3缓存解决循环依赖。
@Autowired进行属性注入可以解决循环依赖。原理:Spring控制bean的生命周期,首先实例化bean,然后注入bean的属性。Spring记录了正在创建的bean(已经实例化但尚未初始化的bean),所以可以重新注入的属性是从记录的ben中依赖它的对象。
相对来说,单独使用构造函数注入并不能解决循环依赖。因为依赖对象需要在构造时传入,所以不能实例化。但是,构造函数注入可以使用@Lazy来解决循环依赖。实例化时传入代理对象,实际使用时会生成真实对象。
00-1010 1.用构造方法注入依赖,A类依赖B类,B类也依赖A类,这样两个类都不能正常创建Bean,会抛出一个异常:beancourrentincreationexception。
@ component public class A { private B B;@ Autowired public A(B B){ this . B=B;} } @ component public class B { private A A;@ Autowired public B(A A){ this . A=A;}}解决方案之一:可以使用lazy annotation来延迟加载依赖项。
@ component public class A { private B B;@ Autowired @ Lazy public A(B B){ this . B=B;} } @ component public class B { private A A;@ Autowired @ Lazy public B(A A){ this . A=A;}}
目录
从日志中可以看出,bean tPartnerOrgService有一个循环依赖。
我在tPartnerOrgService中使用了@Aysnc注释进行异步处理,由@Aysnc注释的bean生成了对象的代理,导致了Spring bean最后没有加载一个原始对象的问题。
解决方案1:用@Lazy注释tPartnerOrgService
解决方案二:代码优化,不要让@Async的Bean参与循环依赖。
一、循环依赖
首先,Spring维护了三张地图,也就是通常所说的三级缓存。
SingletonObjects:俗称singleton pool,缓存singleton Bean singleton factories创建:映射创建的Bean的原始工厂,earlySingletonObjects:映射Bean的早期引用,也就是说这个映射中的Bean不是完整的,只是实例化的。但是,Spring还没有初始化。它通过三级缓存解决循环依赖,其中一级缓存为singletonObjects,二级缓存为早期暴露对象,三级缓存为早期暴露对象工厂。
当A和B两个类被循环引用时,在A实例化之后,用实例化的对象创建一个对象工厂,这个对象工厂被添加到三级缓存中。如果A用AOP表示,那么A表示的对象就是通过这个工厂获得的,如果A不是用AOP表示的。
代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。
紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!
简单一句话说:先去缓存里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。
针对上面的@Aysnc注解产生的循环依赖进行分析:
有@Aysnc注解的bean最后生成了一个代理对象,我们结合Spring bean创建的流程来分析这次问题。
beanA
开始初始化,beanA
实例化完成后给beanA
的依赖属性beanB
进行赋值beanB
开始初始化,beanB
实例化完成后给beanB
的依赖属性beanA
进行赋值因为beanA
是支持循环依赖的,所以可以在earlySingletonObjects
中可以拿到beanA
的早期引用的,但是因为beanB
打了@Aysnc注解
并不能在earlySingletonObjects
中可以拿到早期引用接下来执行执行initializeBean(Object existingBean, String beanName)
方法,这里beanA
可以正常实例化完成,但是因为beanB
打了@Aysnc注解
,所以向Spring IOC容器中增加了一个代理对象,也就是说beanA
的beanB
并不是一个原始对象,而是一个代理对象接下来进行执行doCreateBean
方法时对进行检测,以下代码有所删减,只保留核心逻辑代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);// 重点在这里,这里会遍历所有依赖的bean,如果beanA依赖beanB和缓存中的beanB不相等// 也就是说beanA本来依赖的是一个原始对象beanB,但是这个时候发现beanB是一个代理对象,就会增加到actualDependentBeansfor (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}// 发现actualDependentBeans不为空,就发生了我们最开始截图的错误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.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
到此这篇关于Springboot详细讲解循环依赖的文章就介绍到这了,更多相关Springboot循环依赖内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。