循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示:
代码层面上很好理解,在 bean 创建过程中 class A
和 class B
又经历了怎样的过程呢?
可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。
一、复现循环依赖问题
Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖
1. 基于xml复现循环依赖
- 定义实体 Bean
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
- 重写
customizeBeanFactory
方法,禁止循环依赖
public class NotAllowCircularXmlApplicationContext extends ClassPathXmlApplicationContext {
public NotAllowCircularXmlApplicationContext(String... configLocations) throws BeansException {
super(configLocations);
}
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 禁止循环依赖
super.setAllowCircularReferences(false);
super.customizeBeanFactory(beanFactory);
}
}
- 配置 xml 文件
public class TestAnnoMain {
public static void main(String[] args) {
testCycleRef();
}
public static void testXmlCycleRef() {
NotAllowCircularXmlApplicationContext ac = new NotAllowCircularXmlApplicationContext("application-cycle.xml");
A bean = ac.getBean(A.class);
System.out.println(bean);
}
}
运行结果:
2. 基于注解 @Autowired
复现循环依赖
- 实体定义
@Component
public class C {
@Autowired
private D d;
}
@Component
public class D {
@Autowired
private C c;
}
- 重写
AnnotationConfigApplicationContext
,禁止循环依赖
public class NotAllowCircularAnnotationConfigApplicationContext extends AnnotationConfigApplicationContext {
public NotAllowCircularAnnotationConfigApplicationContext(Class... annotatedClasses) {
super();
register(annotatedClasses);
// 禁止循环依赖
super.setAllowCircularReferences(false);
refresh();
}
}
- 运行结果
public class TestAnnoMain {
public static void main(String[] args) {
testCycleRef();
}
public static void testCycleRef() {
NotAllowCircularAnnotationConfigApplicationContext ac = new NotAllowCircularAnnotationConfigApplicationContext(BeanConfig3.class);
C bean = ac.getBean(C.class);
System.out.println(bean);
}
}
二、在 Spring 中是如何解决该问题的呢?
Spring 中利用三级缓存解决循环依赖问题,缓存定义在 DefaultSingletonBeanRegistry
中
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// 一级缓存,保存 beanName 和 实例化、初始化好的完整 bean 对象
private final Map singletonObjects = new ConcurrentHashMap(256);
// 二级缓存,保存 beanName 和 未初始化的 bean 对象
private final Map earlySingletonObjects = new HashMap(16);
// 三级缓存,保存 beanName 和 lambda 表达式 () -> getEarlyBeanReference(beanName, mbd, bean)
private final Map