何为循环依赖
- 自己依赖自己
- 两个对象相互依赖
- 多个对象依赖成一圈
循环依赖得场景
public class A {
private B b;
public A(B b) {
this.b = b;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
public B(A a) {
this.a = a;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
private A a;
@Async
public void test1() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="a" class="com.wfg.spring.xmlstrat.A" >
<constructor-arg name="b" ref="b"></constructor-arg>
<!-- <property name="b" ref="b"></property>-->
</bean>
<bean name="b" class="com.wfg.spring.xmlstrat.B">
<!-- <constructor-arg name="a" ref="a"/>-->
<property name="a" ref="a"/>
</bean>
</beans>
单例setter 注入 和 多例setter注入
这个又分一下几个一下几个情况:
AB都是单例情况
AB其中有一个是单例模式
AB全部是多例模式
ps: 单例是在容器初始化时就进行创建好对象放入一级缓存中,多利模式的不进行创建只有使用的时候才进行创建
添加 scope=“prototype” 就是多例;
单例模式:
AB全是多例模式
使用源码分析
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
从这个图片上可以看出spring容器初始化时只加载单例模式的,这个地方说明:AB都是多例情况下不会被加载
但是为啥AB其中有一个为单例的情况下又可以了,如果其中有一个是单例的情况下这个入口就可以进入,
流程就演变成下面的情况了:
二级缓存的缓存的意义何在?
从上面我们分析来看二级缓存的意义没啥用处,我们这个举例非常简单如果演变成下面的
当其中一个类一单从三级缓存中创建对象后即将该对象放入二级缓存中(ps此时该对象还没初始化完成,属于正在创建中),如果后面在有其他对象需要此对象直接从二级缓存中获取(ps如果从三级缓存中获取对象就不是单例了)
有源码我们可以知道一旦从二级缓存中获取到了也就不用从三级获取了:
ps: 循环依赖主要是靠的二级缓存,三级缓存的主要是解决bean能进行增强得,不是为了解决循环依赖的问题
构造器注入
从图中的流程看出构造器注入没能添加到三级缓存,也没有使用缓存,所以也无法解决循环依赖问题.和全部是多例情况差不多
单例代理对象setter注入
这种注入方式其实也比较常用,比如平时使用:@Async注解的场景,会通过AOP自动生成代理对象。
@Component
public class A {
@Autowired
private B b;
@Async
public void test1() {
}
}
在类A中添加
@Async
public void test1() {
}
启动报错:
说白了,bean初始化完成之后,后面还有一步去检查:第二级缓存 和 原始对象 是否相等。由于它对前面流程来说无关紧要,所以前面的流程图中省略了,但是在这里是关键点,我们重点说说:
如果增加这一段代码放到B中或者将A的名字修改成C/D再B之后的就可以了
what? 这又是为什么?这就要从spring的bean加载顺序说起了,默认情况下,spring是按照文件完整路径递归查找的,按路径+文件名排序,排在前面的先加载。所以A比B先加载,而改了文件名称之后,B比C先加载。
DependsOn循环依赖
@DependsOn 或者xml中配置dependson属性
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
该方法中:
此处说明正在创建的类和自己依赖的类存在相互依赖直觉报错.