一、Spring的执行流程
在了解spring的循环依赖前,首先应该知道Spring的基本脉络:
二、循环依赖问题
1.循环依赖问题产生的原因
如下图所示(简单来说就是A类的构造函数需要B对象,而B类的构造函数需要A对象)
2.出现循环依赖的案例:
比方说有A和B两个类,A依赖B,B也依赖于A:
public class A {
private B b;
public A(B b){
this.b = b;
}
}
public class B {
private A a;
public B(A a){
this.a = a;
}
}
<bean name="a" class="beans.A">
<constructor-arg ref="b"></constructor-arg>
</bean>
<bean name="b" class="beans.B">
<constructor-arg ref="a"></constructor-arg>
</bean>
当执行new ClassPathXmlApplicationContext(application.xml)
时,会产生下图所示的报错信息:
三、Spring解决循环依赖的办法
方法1.使用默认的singleton单例模式+set注入+三级缓存
//下面只展示A类,B类也会做出类似的改变
public class A {
private B b;
//A有默认的无参构造方法!
public void setB(B b) {
this.b = b;
}
}
<bean name="a" class="beans.A">
<property name="b" ref="b"></property>
</bean>
<bean name="b" class="beans.B">
<property name="a" ref="a"></property>
</bean>
经过上面的改造,循环依赖就会解决,下面的是对源码的剖析过程:
Spring源码中三级缓存如下图所示:
在Spring的refresh(finishBeanFactoryInitialization)方法中,会完成IOC容器中所有单例对象的创建。在doCreateBean方法
中首先会通过createBeanInstance方法
完成a对象的实例化操作(反射clazz.getConstructor().newInstance()),完成实例化操作后,会将该实例化对象形成lambda表达式存放于三级缓存中,在populateBean方法
里,会接着对a对象进行相关属性赋值操作,其中就包含了引用类型b的赋值操作,由于单例池中还没有B类型的对象,于是spring就会接着创建b对象,完成b的实例化操作,也会将b的实例化对象放在三级缓存中,在对b对象进行populateBean
属性赋值时,会将存放于三级缓存中的a对象取出并放在二级缓存里(此时a对象的b属性还没有赋值,a此时并不是一个完整的对象),在b对象完成了所有的属性填充后,执行invokeAwareMethods、BeanPostProcessor、init-method后,会将b对象放在一级缓存中并将三级缓存里的b对象移除。
当b对象创建完成后,a对象即可继续populateBean
,从一级缓存里把b对象取出完成b属性的填充。于是循环依赖问题解决!!!!!!
bean的初始化流程细节如下,红色代表的是最后销毁的对象:
2.比较特殊的循环依赖问题
//xml中的扫描器
<context:component-scan base-package="beans"></context:component-scan>
//配置类中的扫描器
@ComponentScan
----------------------------------------------------------------------
@Component
public class A {
@Autowired
private B b;
public A(B b){
this.b = b;
}
}
@Component
public class B {
@Autowired
private A a;
public B(A a){
this.a = a;
}
}
以上的代码会出现循环依赖问题,解决办法有两种:
方法1:删除上面的有参构造方法 或者 新增一个无参构造方法(本质都是让A和B具有无参构造),如下方所示:
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
在方法1的基础上有个特殊的案例:当某个类的方法中标注了@Async的注解,会产生报错问题!
//xml中的配置支持@Async
<task:annotation-driven executor="annotationExecutor" />
<!-- 支持 @Async 注解 -->
<task:executor id="annotationExecutor" pool-size="20"/>
//配置类中配置支持@Async
@EnableAsync
----------------------------------------------------------------------------------
//仅仅是给A类添加了一个@Async标注的方法,就会报错
@Component
public class A {
@Autowired
private B b;
@Async
public void test(){
System.out.println(b);
}
}
其主要原因是因为a对象会被AsyncAnnotationBeanPostProcessor后置处理器替换成一个新的代理对象,巴拉巴拉导致的(留个记号,博主以后补充!!)
解决办法就是在b属性上加上@Lazy注解,如下发:
@Component
public class A {
@Autowired
@Lazy
private B b;
@Async
public void test(){
System.out.println(b);
}
}
方法2:在上面的有参构造上面添加@Lazy注解也能解决循环依赖问题,如下图所示:
//B类中也需要做出相应的改变!
@Component
public class A {
@Autowired
private B b;
@Lazy
public A(B b){
this.b = b;
}
}
加上@Lazy注解可以保证使用b对象时才会给A类注入b属性。