spring boot循环依赖,spring循环依赖到底怎么解决的

  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容器中增加了一个代理对象,也就是说beanAbeanB并不是一个原始对象,而是一个代理对象接下来进行执行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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • spring编程式事务处理,spring编程事务
  • spring编程式事务处理,spring编程事务,详解Spring学习之编程式事务管理
  • spring的核心功能模块有几个,列举一些重要的spring模块
  • spring的核心功能模块有几个,列举一些重要的spring模块,七个Spring核心模块详解
  • spring注解和springmvc的注解,SpringMVC常用注解
  • spring注解和springmvc的注解,SpringMVC常用注解,详解springmvc常用5种注解
  • spring实现ioc的四种方法,spring的ioc的三种实现方式
  • spring实现ioc的四种方法,spring的ioc的三种实现方式,简单实现Spring的IOC原理详解
  • spring事务失效问题分析及解决方案怎么做,spring 事务失效情况
  • spring事务失效问题分析及解决方案怎么做,spring 事务失效情况,Spring事务失效问题分析及解决方案
  • spring5.0新特性,spring4新特性
  • spring5.0新特性,spring4新特性,spring5新特性全面介绍
  • spring ioc以及aop原理,springmvc aop原理
  • spring ioc以及aop原理,springmvc aop原理,深入浅析Spring 的aop实现原理
  • Spring cloud网关,spring cloud zuul作用
  • 留言与评论(共有 条评论)
       
    验证码: