本质上告诉你springbean注入为什么有些不能有循环依赖
网上有很多博主,要么就是表面的解释下死循环的不可行,要么就是搬出源代码分析为什么会抛出BeanInCreationException,
都没有从本质上解释spring为什么不能有循环依赖,循环依赖难道真的就因为感觉造成“死循环”,就认为任何地方不可以“循环依赖”了吗?
于是带着问题,不断翻阅资料,但是总是找不出为什么不能有循环依赖的解释,这才有了本质上的解决了心中的why冲动!以下就是我从本质上思考的一些心得把。
下面我用构造函数和属性注入两种方式,分别解释为什么循环依赖跟bean的模式,有些有关,有些又无关呢?
-
.构造函数注入
构造函数注入bean,不管单例还是原型,会构成循环依赖是因为:
<bean id="mixSingletonA" class="ccc.spring.circulardependencies.constructor.A" lazy-init="true">
<constructor-arg name="b" ref="mixPrototypeB"/>
</bean>
<bean id="mixPrototypeB" class="ccc.spring.circulardependencies.constructor.B" scope="prototype">
<constructor-arg name="a" ref="mixSingletonA"/>
</bean>
通过构造函数创建A时,大家都知道,必须先创建B,这时候创建一个B至少需要创建一个A,但注意,此时其实一个A都还没有创建呢,所以前面的B也创建不了啊,所以A,B就一直创建不了,如果更大的闭环,其实也是一个都创建不了了。
这里非常要注意,“至少一个”,这几个字眼,这是结合分析单例和原型模式了,还有就是它的原因是,因为一个都创建不了了,而不是什么“死循环”的原因了。其实有点像”死锁“的感觉,相互制约,谁都创建不了,所以也就不能创建bean了
2.属性注入
2.1 再说下setter注入,其中一个是原型模式,也不能有循环依赖的
<bean id="mixSingletonA"
class="ccc.spring.circulardependencies.field.A" lazy-init="true">
<property name="b" ref="mixPrototypeB"/>
</bean>
<bean id="mixPrototypeB" class="ccc.spring.circulardependencies.field.B"
scope="prototype">
<property name="a" ref="mixSingletonA"/>
</bean>
如果是属性注入,先会创建完A,需要依赖B,然后再创建完B,发现又需要A,又需要创建好一个A,由于是prototype模式,是可以创建多个A,B的,结果又会创建B,创建完B,又要创建A…如此循环,根本就不会结束了,这才是真的死循环的原因。
这里要注意字眼,“创建完”,这里的意思就是已经创建好了,而不是将要创建,网上很多没有把这种先后顺序说清楚,导致不能理解构造注入和属性注入的区别。
如果是全是单例模式,为什么就不会了呢,因为,创建B不会再需要创建A了,直接取原来创建的A依赖进去就可以了,也就不会再创建A,继而创建B,一直下去,造成死循环了,所以,其实就是相比prototype模式,不会造成死循环。
至此就把spring框架为什么一定要解决这几种循环依赖的原因彻底说清楚了。一种构造函数实例化对象的机制造成的,一种是要达成prototype模式的设计目的造成的!
所以,这些都是在spring框架设计之初,就已经想好的。而不仅是spring组织是用代码来这样定义告诉我们就要这么用。而是spring组织也必须,遵循这样的java机制和设计,才能完善一个技术的实现规范,也就是实现这样一项定义,事先就需要想好它一些不能存在的场景,从而从设计和代码逻辑上面一开始就避免掉。因为如果是写好的代码运行再报错而不一开始就检测出来,无限个循环创建对象,早就把服务干死了!