什么是循环依赖
循环依赖指的是两个bean互相依赖彼此,beanA在示例化的时候要注入beanB,beanB在实例化的时候要注入beanA,导致两者互相依赖谁都不能实例化成功的情况。
但是,在spring中,如果我们创建了两个bean,彼此注入对方,在spring上下文初始化的时候却可以帮我们构建出两个完成体的springbean,那spring是怎么做到的呢?
解决循环依赖的方法
在实际探索源码的过程中,发现spring解决这个问题主要是采用了三级缓存解决了彼此依赖的问题,下面让我们回顾一下流程。
首先,定义两个bean
package com.zt.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TestBeanA {
@Autowired
TestBeanB testBeanB;
}
package com.zt.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TestBeanB {
@Autowired
TestBeanA testBeanA;
}
可以看到,beanA和beanB互相持有彼此,接下来我们通过ApplicationContext获取testBeanA。
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CircleDependTest {
@Test
public void testCircleDepend() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.zt.beans");
Object testBeanA = applicationContext.getBean("testBeanA");
System.out.println(testBeanA);
}
}
输出结果
TestBeanA创建成功。
三级缓存
一级缓存:singletonObjects 存储构建完成的spring bean对象
二级缓存:earlySingletonObjects 存储三级缓存加工过的对象,此时spring bean 还未构建完成
三级缓存:singletonFactories 存储刚刚构建的bean工厂,实际是一个回调函数。
BeanA构建流程
1、调用beanA的无参构造方法,将beanA放入三级缓存中,标记beanA正在构建中
2、为beanA属性赋值,此时发现,beanA依赖于beanB,从IOC容器中获取beanB
3、beanB此时还为构建,调用beanB的无参构造方法,将beanB放入三级缓存中
4、为beanB属性赋值,此时发现,beanB依赖于beanA,从IOC容器中获取beanA
5、beanA此时正在构建中,从三级缓存中获取beanA,将beanA从三级缓存移除,放入二级缓存中(此步骤可以拓展)
6、将未完全构建完成的beanA赋值给beanB,beanB构建完成,讲beanB放入一级缓存中,并清空二级、三级缓存中的beanB(此步骤spring会统一再次清空缓存)
7、beanA获取到构建完成的beanB,讲beanB赋值给beanA,beanA构建完成,将beanA放入一级缓存中,beanA构建完成。