Spring 能解决所有循环依赖吗?

这篇具有很好参考价值的文章主要介绍了Spring 能解决所有循环依赖吗?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

以下内容基于 Spring6.0.4。

看了上篇文章的小伙伴,对于 Spring 解决循环依赖的思路应该有一个大致了解了,今天我们再来看一看,按照上篇文章介绍的思路,有哪些循环依赖 Spring 处理不了。

严格来说,其实也不是解决不了,所有问题都有办法解决,只是还需要额外配置,这个不是本文的主题,松哥后面再整文章和小伙伴们细聊。

1. 基于构造器注入

如果依赖的对象是基于构造器注入的,那么执行的时候就会报错,代码如下:

@Service
public class AService {
    BService bService;

    public AService(BService bService) {
        this.bService = bService;
    }
}
@Service
public class BService {
    AService aService;

    public BService(AService aService) {
        this.aService = aService;
    }
}

运行时报错如下:

Spring 能解决所有循环依赖吗?,spring,java,后端

原因分析:

上篇文章我们说解决循环依赖的思路是加入缓存,如下图:

Spring 能解决所有循环依赖吗?,spring,java,后端

我们说先把 AService 原始对象创建出来,存入到缓存池中,然后再处理 AService 中需要注入的外部 Bean 等等,但是,如果 AService 依赖的 BService 是通过构造器注入的,那就会导致在创建 AService 原始对象的时候就需要用到 BService,去创建 BService 时候又需要 AService,这样就陷入到死循环了,对于这样的循环依赖执行时候就会出错。

更进一步,如果我们在 AService 中是通过 @Autowired 来注入 BService 的,那么应该是可以运行的,代码如下:

@Service
public class AService {
    @Autowired
    BService bService;
}
@Service
public class BService {
    AService aService;

    public BService(AService aService) {
        this.aService = aService;
    }
}

上面这段代码,AService 的原始对象就可以顺利创建出来放到缓存池中,BService 创建所需的 AService 也就能从缓存中获取到,所以就可以执行了。

2. prototype 对象

循环依赖双方 scope 都是 prototype 的话,也会循环依赖失败,代码如下:

@Service
@Scope("prototype")
public class AService {
    @Autowired
    BService bService;
}
@Service
@Scope("prototype")
public class BService {
    @Autowired
    AService aService;
}

这种循环依赖运行时也会报错,报错信息如下(跟前面报错信息一样):

Spring 能解决所有循环依赖吗?,spring,java,后端

原因分析:

scope 为 prototype 意思就是说这个 Bean 每次需要的时候都现场创建,不用缓存里的。那么 AService 需要 BService,所以就去现场创建 BService,结果 BService 又需要 AService,继续现场创建,AService 又需要 BService…,所以最终就陷入到死循环了。

3. @Async

带有 @Async 注解的 Bean 产生循环依赖,代码如下:

@Service
public class AService {
    @Autowired
    BService bService;

    @Async
    public void hello() {

    }
}
@Service
public class BService {
    @Autowired
    AService aService;

}

报错信息如下:

Spring 能解决所有循环依赖吗?,spring,java,后端

其实大家从这段报错信息中也能看出来个七七八八:在 BService 中注入了 AService 的原始对象,但是 AService 在后续的处理流程中被 AOP 代理了,产生了新的对象,导致 BService 中的 AService 并不是最终的 AService,所以就出错了!

那有小伙伴要问了,上篇文章我们不是说了三级缓存就是为了解决 AOP 问题吗,为什么这里发生了 AOP 却无法解决?

如下两个前置知识大家先理解一下:

第一:

其实大部分的 AOP 循环依赖是没有问题的,这个 @Async 只是一个特例,特别在哪里呢?一般的 AOP 都是由 AbstractAutoProxyCreator 这个后置处理器来处理的,通过这个后置处理器生成代理对象,AbstractAutoProxyCreator 后置处理器是 SmartInstantiationAwareBeanPostProcessor 接口的子类,并且 AbstractAutoProxyCreator 后置处理器重写了 SmartInstantiationAwareBeanPostProcessor 接口的 getEarlyBeanReference 方法;而 @Async 是由 AsyncAnnotationBeanPostProcessor 来生成代理对象的,AsyncAnnotationBeanPostProcessor 也是 SmartInstantiationAwareBeanPostProcessor 的子类,但是却没有重写 getEarlyBeanReference 方法,默认情况下,getEarlyBeanReference 方法就是将传进来的 Bean 原封不动的返回去。

第二:

在 Bean 初始化的时候,Bean 创建完成后,后面会执行两个方法:

  • populateBean:这个方法是用来做属性填充的。
  • initializeBean:这个方法是用来初始化 Bean 的实例,执行工厂回调、init 方法以及各种 BeanPostProcessor。

大家先把这两点搞清楚,然后我来跟大家说上面代码的执行流程。

  1. 首先 AService 初始化,初始化完成之后,存入到三级缓存中。
  2. 执行 populateBean 方法进行 AService 的属性填充,填充时发现需要用到 BService,于是就去初始化 BService。
  3. 初始化 BService 发现需要用到 AService,于是就去缓存池中找,找到之后拿来用,但是!!!这里找到的 AService 不是代理对象,而是原始对象。因为在三级缓存中保存的 AService 的那个 ObjectFactory 工厂,在对 AService 进行提前 AOP 的时候,执行的是 SmartInstantiationAwareBeanPostProcessor 类型的后置处理器 中的 getEarlyBeanReference 方法,如果是普通的 AOP,调用 getEarlyBeanReference 方法最终会触发提前 AOP,但是,这里执行的是 AsyncAnnotationBeanPostProcessor 中的 getEarlyBeanReference 方法,该方法只是返回了原始的 Bean,并未做任何额外处理。
  4. 当 BService 创建完成后,AService 继续初始化,继续执行 initializeBean 方法。
  5. 在 initializeBean 方法中,执行其他的各种后置处理器,包括 AsyncAnnotationBeanPostProcessor,此时调用的是 AsyncAnnotationBeanPostProcessor 的 postProcessAfterInitialization 方法,在该方法中为 AService 生成了代理对象。
  6. 在 initializeBean 方法执行完成之后,AService 会继续去检查最终的 Bean 是不是还是一开始的 Bean,如果不是,就去检查当前 Bean 有没有被其他 Bean 引用过,如果被引用过,就会抛出来异常,也就是上图大家看到的异常信息。

好啦,这就是松哥和大家分享的三种 Spring 默认无法解决的循环依赖,其实也不是无法解决,需要一些额外配置也能解决,当然,这些额外配置并非本文重点,松哥后面再来和大家介绍~

另外最近两篇关于循环依赖的文章都还没有涉及到源码分析,大家先把思路整清楚,后面松哥再出源码分析的文章~文章来源地址https://www.toymoban.com/news/detail-583918.html

到了这里,关于Spring 能解决所有循环依赖吗?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring怎么解决循环依赖问题?

    循环依赖是指 一个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成一个环形调用 , 举个例子 : A 依赖B , B依赖C , C依赖A , 这样就形成了循环依赖;   ①构造器的循环依赖:这种依赖spring是处理不了的,直接拋出BeanCurrentlyInCreationException异常。 ②单例模式下的se

    2024年02月08日
    浏览(50)
  • Spring如何解决循环依赖问题

    循环依赖问题在Spring中主要有三种情况: (1)通过构造方法进行依赖注入时产生的循环依赖问题。 (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。 (3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。 在Spring中,只有第

    2024年02月06日
    浏览(44)
  • 三级缓存---解决 Spring 循环依赖

    首先,什么是循环依赖?这个其实好理解,就是两个 Bean 互相依赖,类似下面这样: \\\"\\\"\\\" \\\"\\\"\\\" AService 和 BService 互相依赖: 一般来说,循环依赖有三种不同的形态,上面就是其中一种。 另外两种分别是三者依赖,如下图: 这种循环依赖一般隐藏比较深,不易发觉。 还有自我依

    2024年02月16日
    浏览(44)
  • Spring 是如何解决循环依赖的

    1.什么是循环依赖? 所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下: 2.通过手写代码演示理解Spring循环依赖 DEMO: 为什么需要二级缓存? 一级缓存和二级缓存相比: 二级缓存只要是为了分

    2024年02月03日
    浏览(39)
  • spring解决循环依赖的三级缓存

    实例化,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法,简单理解就是new了一个对象。 属性注入,对应方法:AbstractAutowireCapableBeanFactory的populateBean方法,为实例化中new出来的对象填充属性和注入依赖。 初始化,对应方法:AbstractAutowireCapableBeanFactory的initialize

    2024年02月03日
    浏览(39)
  • Spring——三级缓存解决循环依赖详解

    就是在Bean生成流程中保存Bean对象三种形态的三个Map集合,如下: 用来解决什么问题? 这个大家应该熟知了,就是循环依赖 什么是循环依赖? 就像下面这样,AService 中注入了BService ,而BService 中又注入了AService ,这就是循环依赖 这几个问题我们结合源码来一起看一下 : 三级

    2024年02月03日
    浏览(38)
  • [Spring] 三级缓存解决循环依赖详解

    注册一个bean对象的过程: Spring扫描class得到BeanDefinition – 根据得到的BeanDefinition去生成bean – 现根据class推断构造方法 – 根据推断出来的构造方法,反射,得到一个对象 – 填充初始对象中的属性(依赖注入) – 如果原始对象种的某个方法被AOP了,那么要根据原始对象生成一

    2024年02月15日
    浏览(40)
  • Spring 怎么解决循环依赖的呢?

    Spring 循环依赖:简单说就是自己依赖自己,或者和别的 Bean 相互依赖。 只有单例的 Bean 才存在循环依赖的情况, 原型 (Prototype)情况下,Spring 会直接抛出异常。原因很简单,AB 循环依赖,A 实例化的时候,发现依赖 B,创建 B 实例,创建 B 的时候发现需要 A,创建 A1 实例……

    2024年02月09日
    浏览(40)
  • Spring 是如何解决循环依赖问题的?

    提示:这里简述项目相关背景: 例如:项目场景:示例:通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 5s 传输一批传感器数据(不是很大)         我们都知道,如果在代码中,将 两个或多个 Bean 互相之间持有对方的引用 就会发生循环依赖。循环的依赖将会导致注入死循环,这

    2024年02月13日
    浏览(40)
  • 【spring6】解决循环依赖的机理

            循环依赖:A对象中有B属性。B对象中有A属性。比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。         解决:至少一个类采用set注入 + singleton模式。         原因:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作

    2024年04月26日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包