Post

Spring解决循环依赖的三种特殊情况

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

Spring 依赖注入可分为:

  • 构造器
  • setter
  • @Autowired
  • @Resource

那么推荐哪个呢?先给结论:@Autowired(优先)和setter注入 其中构造器注入时无法利用三级缓存来解决循环依赖问题;@Resource在存在同名Bean时会抛异常。

Spring利用三级缓存解决了循环依赖问题,设AService和BService相互注入,其循环依赖注入流程如下:

cycle

以下3种不当使用会导致循环依赖报错:

1. 构造器注入

而使用构造器注入时,无法实例化原始AService,则无法存入缓存池,进而导致循环依赖报错。可以通过@Lazy注解解决,其原理是先构建代理进行占位,使用时再去Bean容器找。

2. 依赖双方 scope = prototype

1
2
3
4
5
6
7
8
9
10
11
12
@Service
@Scope("prototype")
public class AService {
    @Autowired
    BService bService;
}
@Service
@Scope("prototype")
public class BService {
    @Autowired
    AService aService;
}

scope为prototype时,每次获取Bean都需要现场创建实例,无需缓存,所以会陷入死循环。

3. Async

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class AService {
    @Autowired
    BService bService;

    @Async
    public void hello() {

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

}

在 BService 中注入了 AService 的原始对象,但是 AService 在后续的处理流程中被 AOP 代理了,产生了新的对象,导致 BService 中的 AService 并不是最终的 AService.

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

第二:

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

populateBean:这个方法是用来做属性填充的。 initializeBean:这个方法是用来初始化 Bean 的实例,执行工厂回调、init 方法以及各种 BeanPostProcessor。 大家先把这两点搞清楚,然后我来跟大家说上面代码的执行流程。

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

This post is licensed under CC BY 4.0 by the author.