什么是循环依赖?
依赖的相互引用,如下列的这种形式
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
Spring是如何解决循环依赖的
Spring是通过三级缓存来解决循环依赖
- singletonObjects : 单例bean,已经实例化,完成属性填充
- earlySingletonObjects : 半成品bean,已经实例化,未完成属性填充
- singletonFactories : 一个函数式接口
为什么要使用三级缓存?
因为Spring的动态代理流程是在属性填充流程之后的,如果只使用二级缓存,半成品bean中不确定是放一个普通对象还是代理对象,所以需要使用三级缓存来保存一个不确定的值
PS : 如果只使用二级缓存,那必须所有对象都进行动态代理,那样会生成很多class文件,浪费内存
图解一般情况下的循环依赖
Spring真的能完全解决循环依赖?
改造一下上文代码
@Component
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
@Component
public class B {
@Autowired
private A a;
}
很遗憾,Spring没能解决这种构造器与setter注入形式的循环依赖,我们来分析具体原因
前置知识点
- 如果一个类有且仅有一个有参构造方法,Spring会以此构造方法实例化对象
- 实例化对象之前,会先处理参数的依赖。比如上述代码中bean A的创建过程中,会先getBean(b),再实例化对象A
源码分析
AbstractBeanFactory#doGetBean
我们先看一下这个getSingleton方法本身,而不是createBean方法
由上述源码我们可以得知:执行createBean方法之前会将beanName加入到singletonsCurrentlyInCreation列表中,表明这个bean正在创建
后续b进行属性填充的时候会先从singletonFactories中获取a,因为a还没有进行实例化,没有放入singletonFactories中,所以又会执行getSingleton方法,因为singletonsCurrentlyInCreation这个列表已经存在a这个beanName,所以if条件成立,抛出异常
图解流程
构造方法与setter注入的循环依赖,如果构造方法对应的bean先处理,则会抛出异常,后处理则不会抛出异常。
注意 : bean A 还没有实例化,singletonFactories中还没有提前暴露的对象
如果将上述代码改下列形式,则不会抛出异常
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
PS :针对A先实例化,B后实例化
综上所述
- setter和setter的循环依赖:正确注入
- 构造方法和setter的循环依赖:抛出异常
- setter和构造方法的循环依赖:正确注入
- 构造方法和构造方法的循环依赖:抛出异常
普通情况下,我们标记bean正在创建,后续会将实例对象提前暴露放入singletonFactories中,但是构造方法和setter方式的循环依赖在标记bean正在创建后,并没有立刻将实例对象放入singletonFactories中,而是处理构造方法中参数所对应的依赖,如果这个参数对应的依赖,循环依赖了当前bean,则会抛出异常。即标记正在创建的bean,在对象提前暴露放入singletonFactories之前,不能注入一个依赖当前bean的bean
Spring如何解决循环依赖?
SpringBoot2.6及以上版本默认不支持循环依赖,那如何解决循环依赖呢?
主要原因就是SpringBoot将allowCircularReferences默认值设置为false
从配置角度
1.在application.properties文件中将该值设置为true
spring.main.allow-circular-references=true
2.使用bfpp将该值设置为true
@Component
public class FirstBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) registry;
defaultListableBeanFactory.setAllowCircularReferences(true);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
从代码角度
1.使用@Lazy注解
@Component
public class A {
@Autowired
@Lazy
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
2.使用@Lookup注解
@Component
public class A {
private B b;
@Lookup
public B getB() {
return this.b;
}
}
@Component
public class B {
@Autowired
private A a;
}
3.实现ApplicationContextAware接口
@Component
public class A implements ApplicationContextAware {
private B b;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.b = applicationContext.getBean(B.class);
}
}
@Component
public class B {
@Autowired
private A a;
}