> 文章列表 > spring5.1+SmartInstantiationAwareBeanPostProcessor 解决循环依赖

spring5.1+SmartInstantiationAwareBeanPostProcessor 解决循环依赖

spring5.1+SmartInstantiationAwareBeanPostProcessor 解决循环依赖

SmartInstantiationAwareBeanPostProcessor 解决循环依赖的过程, 例如上面的 A依赖B, B依赖A

SmartInstantiationAwareBeanPostProcessor 是 Spring 中的一个接口,它扩展了 InstantiationAwareBeanPostProcessor 接口并提供了对 Bean 的实例化和属性填充的更高级的控制。在 Spring Boot 项目中,SmartInstantiationAwareBeanPostProcessor 通过三级缓存机制解决了循环依赖的问题。以 A 依赖 B,B 依赖 A 为例,我们来了解下这个过程:

  1. 当容器开始实例化 A 时,首先会调用 SmartInstantiationAwareBeanPostProcessor#predictBeanType 方法来预测 Bean 的类型。如果可以预测到类型,Spring 会优先使用这个类型来实例化对象。

  2. 接下来会调用 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference 方法获取 A 的提前暴露的引用。这个引用通常是一个代理对象,它可以处理 A 对 B 的依赖。

  3. 将 A 的提前暴露引用放入三级缓存。三级缓存分别是 singletonObjects、earlySingletonObjects 和 singletonFactories。其中,singletonObjects 用于存放完全初始化好的单例 Bean,earlySingletonObjects 用于存放提前暴露的 Bean 引用,singletonFactories用于存放原始 Bean 的工厂对象。在这个例子中,A 的提前暴露引用会被放入 earlySingletonObjects 缓存。

  4. 接下来,容器开始实例化 B。同样地,会调用 SmartInstantiationAwareBeanPostProcessor#predictBeanType 和 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference 方法获取 B 的提前暴露引用,并将其放入 earlySingletonObjects 缓存。

  5. 当 B 的实例化完成后,开始填充属性。这时会发现 B 依赖 A,于是会从三级缓存中获取 A 的引用。由于 A 正在创建过程中,所以从 earlySingletonObjects 缓存中获取到了 A 的提前暴露引用。将 A 的引用设置到 B 的属性上,完成 B 的属性填充。

  6. B 的实例化和属性填充完成后,将 B 从 earlySingletonObjects 移动到 singletonObjects 缓存中。

  7. 接下来,开始填充 A 的属性。由于 A 依赖 B,会从三级缓存中获取 B 的引用。此时,B 已经完全初始化完成,所以从 singletonObjects 缓存中获取到了 B 的引用。将 B 的引用设置到 A 的属性上,完成 A 的属性填充。

  8. A 的实例化和属性填充完成后,将 A 从 earlySingletonObjects 移动到 singletonObjects 缓存中。至此,A 和 B 的循环依赖解决了。

  9. 容器会继续调用各个 BeanPostProcessor 的 postProcessAfterInitialization 方法,对 A 和 B 进行后置处理。这些后置处理可能包括代理对象的创建等操作。

  10. 最后,A 和 B 的实例化和属性填充完成,循环依赖被成功解决。容器将 A 和 B 的引用提供给其他需要它们的 Bean。

通过以上流程,Spring Boot 利用 SmartInstantiationAwareBeanPostProcessor 和三级缓存机制成功地解决了 A 和 B 之间的循环依赖问题。这种处理方式不仅保证了 Bean 实例化的正确性,还确保了容器在解决循环依赖时的高性能。

总结

SmartInstantiationAwareBeanPostProcessor 是一个 Spring 容器中的扩展点,它本身并不直接解决循环依赖问题。Spring 容器在解决循环依赖时,主要依赖于 DefaultSingletonBeanRegistry 类中的 getSingleton() 方法,该方法使用了三级缓存来处理循环依赖。
上述过程仅适用于属性注入的情况
SmartInstantiationAwareBeanPostProcessor,它提供了一些扩展点,允许我们在 Spring 容器创建和初始化 Bean 实例的过程中进行干预。例如,我们可以使用它来改变实例化策略、解析依赖关系或者修改属性值等。然而,SmartInstantiationAwareBeanPostProcessor 本身并不直接参与解决循环依赖问题。

总之,Spring 容器通过三级缓存解决属性注入的循环依赖问题。然而,对于构造器注入的循环依赖,我们需要调整代码结构或使用其他技术手段来解决。SmartInstantiationAwareBeanPostProcessor 是一个扩展点,允许我们在 Bean 实例创建和初始化的过程中进行干预,但它本身并不直接解决循环问题

在实际项目中,我们应尽量避免产生循环依赖,以降低代码的复杂度。当循环依赖确实需要解决时,可以尝试以下方法:

使用属性注入而非构造器注入,让 Spring 容器自动处理循环依赖问题。
提取接口,将 A 和 B 的公共功能抽取为接口,然后让 A 和 B 分别依赖这些接口,而不是直接依赖彼此。
使用事件驱动或消息队列,将 A 和 B 之间的交互转移到事件驱动或消息队列中,这样可以避免直接的循环依赖。
调整代码结构,如使用中介者模式、模块化设计等方法,将循环依赖拆分为线性依赖