Spring解决循环依赖的几种方式
本文作者:FUNKYE(陈健斌),杭州某互联网公司主程。
起因
先上引起循环依赖的demo
1 |
|
通过分析可以发现,A依赖于B,B依赖C,C又依赖A.这就是个循环依赖的例子,但是由于A的构造方法内依赖了B,这时候启动会出现如下异常:
1 | Description: |
通过上一篇博客,我们可以知道,spring内部的三级缓存机制来解决了属性循环依赖的问题,但是为什么在构造方法内依赖时会出现以上的异常呢?
因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决.这时候需要我们人为的去解决循环依赖的问题.
解决方案
1.使用 @Lazy
1 |
|
如上这样处理,我的启动项目时会发现不会在出现异常的情况了,那么@Lazy又帮我们做了什么呢?
在默认情况下,Spring会在应用程序上下文的启动时创建所有单例bean。这背后的原因很简单:立即避免和检测所有可能的错误,而不是在运行时。但是这样一来我们的A这个实例在初始化的时候,由于构造方法的存在,他会找不到B实例,又由于A构造方法没有完成无法进入三级缓存,导致上面出现了异常.而@Lazy就是一个延时加载,在启动的过程中,他会注入一个B的代理,等首次使用的时候才会完全初始化.
2.使用 Setter/Field 注入
1 |
|
改为set注入B的实例后,我们会发现启动也是顺利的,因为这种方式创建Bean,实际上它此时的依赖并没有被注入,只有当你使用他的时候,他才会去完成这个实例的注入.
总结
以上介绍了两种最常用的解决方案,其实还有几种,比如:
1.@PostConstruct,A依赖B,B依赖A时,使用 @PostConstruct,把A需要的B注入进来时,顺便把B的A给set进去(需要A来主动注入到B,所以不是很推荐),
2.实现ApplicationContextAware and InitializingBean接口(太过复杂不如一个@lazy来的爽快)