Spring循环依赖
我是 javapub,一名 Markdown
程序员从👨💻,八股文种子选手。
: 小伙子,听说你对 Spring 循环依赖比较熟悉,是真的吗?
王哥,不敢说很熟悉,但有一定了解,请王哥考考我。
: 那好,首先简单说一下,什么是 Spring 循环依赖?
Spring 循环依赖指的是 BeanA 依赖 BeanB,而 BeanB 也直接或间接依赖 BeanA,两者之间形成依赖循环。这会导致 Bean 无法正常实例化。
: circular dependencies,确实是这样。Spring Bean 的循环依赖有哪些场景?
主要有三种场景:
- 构造器循环依赖:BeanA 的构造器注入 BeanB,BeanB 的构造器又注入 BeanA。
- Setter 循环依赖:BeanA 在 setter 方法中注入 BeanB,BeanB 的 setter 方法又注入 BeanA。
- 代理循环依赖:BeanA 依赖 BeanB 的代理对象,BeanB 依赖 BeanA 的代理对象。
: 那 Spring 是如何解决循环依赖问题的呢?
Spring 采用提前暴露对象的方式解决循环依赖,主要通过 AOP 功能和 SmartInstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 方法实现。
- 对代理循环依赖,Spring 会首先创建目标对象,然后再创建代理对象。
- 对 Setter 循环依赖,Spring 会在对象创建完成后,提前将对象注入到 BeanFactory 中。然后在注入依赖时,直接从 BeanFactory 中获取已经提前实例化的对象。
//AbstractAutowireCapableBeanFactory#doCreateBean
...
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) {
return bean;
}
...
// Allow post-processors to modify the merged bean definition.
bean = applyBeanPostProcessorsAfterInstantiation(bean, beanName);
...
- 对构造器循环依赖,Spring 在实例化对象后,会提前暴露一个 ObjectFactory,再从 ObjectFactory 中获取目标对象完成依赖注入。
: 那么,Spring 循环依赖会带来什么问题?
Spring 循环依赖主要会带来以下问题:
- 破坏 Bean 的单例性。因为循环依赖导致一个 Bean 被实例化多次,破坏了 Spring 容器中 Bean 的唯一性。
- 容器无法正常结束注入过程。循环依赖会导致 Bean 无法完全实例化,一直在循环注入过程中,无法结束。
- 降低程序的可读性和维护性。循环依赖关系使得程序难以理解,也难以维护。
: 那么,如何避免 Spring 循环依赖呢?
主要有以下几种方式可以避免 Spring 循环依赖:
- 编程方式避免。在 Bean 中提供 set 方法,但不在构造器中注入依赖。这样只有在 Bean 完全实例化后,才会注入依赖对象。
- 构造器注入避免。只使用构造器注入,不用 Setter 方法注入。因为构造器是在 Bean 实例化阶段完成的,避免了循环依赖。
- 避免过于提前暴露 Bean。如果一个 Bean 不需要频繁使用,不要将其设置为 Singleton,推迟其实例化时间。
- 分模块避免。将循环依赖的 Bean 拆分到不同的模块中,模块间采用接口隔离,避免循环依赖。
- 采用重构手段。如果上述方式都不可行,那么需要通过重构来避免循环依赖。比如拆分过于庞大的 Bean 为多个小 Bean 等。
: 不错,总结得很全面。看来你对 Spring 循环依赖还是比较清晰的,加油!
谢谢 王哥 的提问,让我对 Spring 循环依赖有了更深入的认识。我会继续努力学习的!
最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解我们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣可以关注公众号JavaPub追更!
🎁目录合集:
Gitee:https://gitee.com/rodert/JavaPub
GitHub:https://github.com/Rodert/JavaPub