来源:江南一点雨 以下内容基于 Spring6.0.4。有循 看了上篇文章的环依小伙伴,对于 Spring 解决循环依赖的有循思路应该有一个大致了解了,今天我们再来看一看,环依按照上篇文章介绍的有循思路,有哪些循环依赖 Spring 处理不了。环依 严格来说,有循其实也不是环依解决不了,所有问题都有办法解决,有循只是环依还需要额外配置,这个不是有循本文的主题,松哥后面再整文章和小伙伴们细聊。环依 如果依赖的有循对象是基于构造器注入的,那么执行的环依时候就会报错,代码如下: { BService bService; { .bService = bService; } } { AService aService; { .aService = aService; } } 运行时报错如下: 原因分析: 上篇文章我们说解决循环依赖的思路是加入缓存,如下图: 我们说先把 AService 原始对象创建出来,存入到缓存池中,然后再处理 AService 中需要注入的外部 Bean 等等,但是,源码下载如果 AService 依赖的 BService 是通过构造器注入的,那就会导致在创建 AService 原始对象的时候就需要用到 BService,去创建 BService 时候又需要 AService,这样就陷入到死循环了,对于这样的循环依赖执行时候就会出错。 更进一步,如果我们在 AService 中是通过 @Autowired 来注入 BService 的,那么应该是可以运行的,代码如下: { BService bService; } { AService aService; { .aService = aService; } } 上面这段代码,AService 的原始对象就可以顺利创建出来放到缓存池中,BService 创建所需的 AService 也就能从缓存中获取到,所以就可以执行了。 循环依赖双方 scope 都是 prototype 的话,也会循环依赖失败,代码如下: ) { BService bService; } ) { AService aService; } 这种循环依赖运行时也会报错,报错信息如下(跟前面报错信息一样): 原因分析: scope 为 prototype 意思就是说这个 Bean 每次需要的时候都现场创建,不用缓存里的。亿华云计算那么 AService 需要 BService,所以就去现场创建 BService,结果 BService 又需要 AService,继续现场创建,AService 又需要 BService...,所以最终就陷入到死循环了。 带有 @Async 注解的 Bean 产生循环依赖,代码如下: { BService bService; { } } { AService aService; } 报错信息如下: 其实大家从这段报错信息中也能看出来个七七八八:在 BService 中注入了 AService 的原始对象,但是 AService 在后续的处理流程中被 AOP 代理了,产生了新的对象,导致 BService 中的 AService 并不是最终的 AService,所以就出错了! 那有小伙伴要问了,上篇文章我们不是说了三级缓存就是为了解决 AOP 问题吗,为什么这里发生了 AOP 却无法解决? 如下两个前置知识大家先理解一下: 第一: 其实大部分的 AOP 循环依赖是没有问题的,这个 @Async 只是一个特例,特别在哪里呢?一般的 AOP 都是由 AbstractAutoProxyCreator 这个后置处理器来处理的云南idc服务商,通过这个后置处理器生成代理对象,AbstractAutoProxyCreator 后置处理器是 SmartInstantiationAwareBeanPostProcessor 接口的子类,并且 AbstractAutoProxyCreator 后置处理器重写了 SmartInstantiationAwareBeanPostProcessor 接口的 getEarlyBeanReference 方法;而 @Async 是由 AsyncAnnotationBeanPostProcessor 来生成代理对象的,AsyncAnnotationBeanPostProcessor 也是 SmartInstantiationAwareBeanPostProcessor 的子类,但是却没有重写 getEarlyBeanReference 方法,默认情况下,getEarlyBeanReference 方法就是将传进来的 Bean 原封不动的返回去。 第二: 在 Bean 初始化的时候,Bean 创建完成后,后面会执行两个方法: 大家先把这两点搞清楚,然后我来跟大家说上面代码的执行流程。 好啦,这就是松哥和大家分享的三种 Spring 默认无法解决的循环依赖,其实也不是无法解决,需要一些额外配置也能解决,当然,这些额外配置并非本文重点,松哥后面再来和大家介绍~ 另外最近两篇关于循环依赖的文章都还没有涉及到源码分析,大家先把思路整清楚,后面松哥再出源码分析的文章~1. 基于构造器注入
2. prototype 对象
3. @Async