Spring 循环依赖

有时候我们又发现在用Spring进行依赖注入时,虽然Bean之间有循环依赖,但是代码本身却大概率能很正常的work,似乎也没有任何bug。很多敏感的同学心里肯定有些犯嘀咕,循环依赖这种触犯因果律的事情怎么能发生呢?

什么是依赖?

所谓A依赖B,可以理解为A中某些功能的实现是需要调用B中的其他功能配合实现的。这里也可以拆分为两7层含义:
A强依赖B:创建A的实例这件事情本身需要B来参加。对照在现实生活就像妈妈生你一样。
A弱依赖B:创建A的实例这件事情不需要B来参加,但是A实现功能是需要调用B的方法。对照在现实生活就像男耕女织一样。
那么,所谓循环依赖,其实也有两层含义:强依赖之间的循环依赖。弱依赖之间的循环依赖。讲到这一层,我想大家应该知道我想说什么了。

什么是依赖调解?

对于强依赖而言,A和B不能互相作为存在的前提,否则宇宙就爆炸了。因此这类依赖目前是无法调解的。
对于弱依赖而言,A和B的存在并没有前提关系,A和B只是互相合作。因此正常情况下是不会出现违反因果律的问题的。
那什么是循环依赖的调解呢?将原本是弱依赖关系的两者误当做是强依赖关系的做法 重新改回弱依赖关系的过程。spring的依赖调解也只是针对弱依赖。

Spring的依赖注入模型(步骤)

这一部分网上有很多相关内容,大概是上面提到的三步:
1、类的构造,调用构造函数、解析强依赖(一般是无参构造),并创建类实例。
2、类的配置,根据Field/GetterSetter中的依赖注入相关注解、解析弱依赖,并填充所有需要注入的类。
3、类的初始化逻辑,调用生命周期中的初始化方法(例如@PostConstruct注解或InitializingBean的afterPropertiesSet方法),执行实际的初始化业务逻辑。

这样,构造函数的功能就由原来的三个弱化为了一个,只负责类的构造。并将类的配置交由DI,将类的初始化逻辑交给生命周期。
思考三个问题:
1、Spring除了构造函数之外还要在Bean生命周期里有一个额外的初始化方法?
2、这个初始化方法和构造函数到底有什么区别?
3、为什么Spring建议将初始化的逻辑写在生命周期里的初始化方法里?
现在,把依赖调解结合起来看,解释就十分清楚了:为了进行依赖调解,Spring在调用构造函数时是没有将依赖注入进来的。也就是说构造函数中是无法使用通过DI注入进来的bean(或许可以,但是Spring并不保证这一点)。如果不在构造函数中使用依赖注入的bean而仅仅使用构造函数中的参数,虽然没有问题,但是这就导致了这个bean强依赖于他的入参bean。当后续出现循环依赖时无法进行调解。

一阶段总结

根据上面的分析我们应该得到了以下共识:
1、通过构造函数传递依赖的做法是有可能造成无法自动调解的循环依赖的。
2、纯粹通过Field/GetterSetter进行依赖注入造成的循环依赖是完全可以被自动调解的。

遇到下面问题

整个依赖链大概是这样
在这里插入图片描述
BeanA,BeanB,ConfigurationA之间有一个循环依赖,所有的依赖都是通过非构造函数注入的方式实现的,理论上似乎可以自动调解的。
但是实际上,这段代码会报下面的错:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

问题的症结就在于ConfigurationA这个配置类
配置类和普通的Bean有一个区别,就在于除了同样作为Bean被管理之外,配置类也可以在内部声明其他的Bean。
样就存在一个问题,配置类中声明的其他Bean的构造过程其实是属于配置类的业务逻辑的一部分的。也就是说我们只有先将配置类的依赖全部满足之后才可以创建他自己声明的其他的Bean。(如果不加这个限制,那么在创建自己声明的其他Bean的时候,如果用到了自己的依赖,则有空指针的风险。)

这样一来,BeanA对ConfigurationA就不再是弱依赖,而是实打实的强依赖了(也就是说ConfigurationA的初始化不仅影响了BeanA的依赖填充,也影响了BeanA的实例构造)。

结论:

除了构造注入会导致强依赖以外,一个Bean也会强依赖于暴露他的配置类。

引用链接
https://mp.weixin.qq.com/s/HHf5BWgE5IikiaH76zFFgQ

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值