Spring 是如何解决循环依赖的?
循环依赖:
Spring 循环依赖有三种情况:
- 构造器的循环依赖,这种依赖 Spring 无法处理,直接抛出 BeanCurrentlyInCreationException 异常
- 单例模式下的 setter 循环依赖,可以通过三级缓存处理
- 非单例循环依赖,无法处理,BeanCurrentlyInCreationException 异常
构造器循环依赖
正要创建的 bean 记录在缓存中,Spring 容器架构一个正在创建的 bean 标识符放在一个 “当前创建 bean 池”中国, 因此如果在创建 Bean 过程中,如果发现已经在当前创建的 Bean 池中,则抛出 BeanCurrentlyInCreationException 异常表示循环依赖,对于创建完毕的 Bean 将从“当前创建 Bean 池”中清除。 先看个例子:
// StudentA
public class StudentA {
private StudentB studentB ;
public void setStudentB(StudentB studentB) {
this.studentB = studentB;
}
public StudentA() {
}
public StudentA(StudentB studentB) {
this.studentB = studentB;
}
}
// StudentB
public class StudentB {
private StudentC studentC ;
public void setStudentC(StudentC studentC) {
this.studentC = studentC;
}
public StudentB() {
}
public StudentB(StudentC studentC) {
this.studentC = studentC;
}
}
// StudentC
public class StudentC {
private StudentA studentA ;
public void setStudentA(StudentA studentA) {
this.studentA = studentA;
}
public StudentC() {
}
public StudentC(StudentA studentA) {
this.studentA = studentA;
}
}
xml 配置
<bean id="a" class="com.student.StudentA">
<constructor-arg index="0" ref="b"></constructor-arg>
</bean>
<bean id="b" class="com.student.StudentB">
<constructor-arg index="0" ref="c"></constructor-arg>
</bean>
<bean id="c" class="com.student.StudentC">
<constructor-arg index="0" ref="a"></constructor-arg>
</bean>
测试代码
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//System.out.println(context.getBean("a", StudentA.class));
}
}
报错如下:
caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Setter 注入(单例)
<!--scope="singleton"(默认就是单例方式) -->
<bean id="a" class="com.student.StudentA" scope="singleton">
<property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.student.StudentB" scope="singleton">
<property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.student.StudentC" scope="singleton">
<property name="studentA" ref="a"></property>
</bean>
测试代码
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(context.getBean("a", StudentA.class));
}
}
运行时不会报错的.
Setter 注入(非单例模式)
对于 prototype 作用域的 Bean ,Spring 容器无法完成依赖注入,因为 Prototype 作用域的bean ,sring 不进行缓冲,无法提提前暴露一个创建中的Bean。会抛出异常。
<bean id="a" class="com.student.StudentA" scope="prototype">
<property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.student.StudentB" scope="prototype">
<property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.student.StudentC" scope="prototype">
<property name="studentA" ref="a"></property>
</bean>
测试代码
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//System.out.println(context.getBean("a", StudentA.class));
}
}
报错
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Spring Bean 创建过程
- 实例化 Bean 对象,createBeanInstance 实例化
- 设置 Bean 属性,populateBean 填充属性
- 通过 各种 Aware 接口声明了依赖关系,则会注入 Bean 对容器基础设施层面的依赖,包括 BeanNameAware、BeanFactoryAware 和 ApplicationContextAware 分别注入 BeanID, BeanFactory 或者 ApplicationContext。
- 调用 BeanPostProcessor 的前置初始化方法 postProcessBeforeInitialization
- 如果实现了 InitializingBean 接口,会调用 afterProperties 方法。
- 调用 Bean 自定义的 init 方法,initializeBean 调用 xml 的 init方法
- 调用 BeanPostprocessor 的后缀初始方法 postProcessAfterInitialization。
- 创建过程完毕。
Spring 是如何解决单例的循环依赖问题的呢?
Spring 采用的三级缓存解决了单例的循环依赖问题。
三级缓存:
Spring 源码 DefaultSingletonBeanRegistry.java 中:
/** Cache of singleton objects: bean name --> bean instance */
一级缓存:维护着所有创建完成的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
二级缓存:维护早期暴露的Bean(只进行了实例化,并未进行属性注入)
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
三级缓存:维护创建中Bean的ObjectFactory(解决循环依赖的关键)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
// 如果是新的bean 也会调用这个方法,这个方法是往一级缓存中set 值的 getSingleton()中也会调用
addSingleton(beanName, singletonObject);
}
}
/**
* 添加单例实例
* 解决循环引用的问题
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 一级缓存实例化 bean 中不包含 正创建的 bean
if (!this.singletonObjects.containsKey(beanName)) {
// 三级缓存中添加
this.singletonFactories.put(beanName, singletonFactory);
// 二级缓冲删除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
AbstractBeanFactory.doGetBean()
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 尝试通过bean名称获取目标bean对象,比如这里的A对象
Object sharedInstance = getSingleton(beanName);
// 我们这里的目标对象都是单例的
if (mbd.isSingleton()) {
// 这里就尝试创建目标对象,第二个参数传的就是一个ObjectFactory类型的对象,这里是使用Java8的lamada
// 表达式书写的,只要上面的getSingleton()方法返回值为空,则会调用这里的getSingleton()方法来创建
// 目标对象
sharedInstance = getSingleton(beanName, () -> {
try {
// 尝试创建目标对象
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
throw ex;
}
});
}
return (T) bean;
}
getSingleton 可以这样理解:
- 先从一级缓冲中看有没有创建好的 bean ,有就直接返回。
- 如果没有,那么从二级缓存中看有没有创建 半成品的 Bean,如果有,直接返回
- 如果没有,从三级缓存中看下有没有创建过程中的 bean,还没有 那么通过 singletonFactory.getObject 最后到 createBean 创建。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 尝试从缓存中获取成品的目标对象,如果存在,则直接返回
Object singletonObject = this.singletonObjects.get(beanName);
// 如果缓存中不存在目标对象,则判断当前对象是否已经处于创建过程中,在前面的讲解中,第一次尝试获取A对象
// 的实例之后,就会将A对象标记为正在创建中,因而最后再尝试获取A对象的时候,这里的if判断就会为true
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 这里的singletonFactories是一个Map,其key是bean的名称,而值是一个ObjectFactory类型的
// 对象,这里对于A和B而言,调用图其getObject()方法返回的就是A和B对象的实例,无论是否是半成品
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 获取目标对象的实例
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
Spring 是怎么解决单例setter注入循环依赖的?
- Spring是通过递归的方式获取目标bean及其所依赖的bean的;
- Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性
setter 注入是属性注入和构造器注入不一样,spring初始化是先创建bean ,然后注入属性的。单例 的setter采用三级缓存各自拿到各自的属性引用,然后再属性注入,最后各自完成实例化,不存在循环等待死锁的问题。
场景:A 依赖 B,B 依赖 A。
假设创建 A 对象的时候进入 getSingleton 方法。创建 B 的时候进入了个 doCreateBean 方法,在创建 B 还没创建完过程中,会在三级缓存 singletonFactories 先放一个 B,此时,如果创建 A 对象时,一级缓存没有B,从二级缓存找,二级缓存没有,从三级别缓存中找到就可以直接返回,并将自身A放入一级缓存中。
此时 B 在初始化过程中,从一级缓存中取到了A,这样B就拿到了A的引用,这样也B也就在拿到A的过程中完成了初始化。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 实例化当前尝试获取的bean对象,比如A对象和B对象都是在这里实例化的
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 判断Spring是否配置了支持提前暴露目标bean,也就是是否支持提前暴露半成品的bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences
&& isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 如果支持,这里就会将当前生成的半成品的bean放到singletonFactories中,这个singletonFactories
// 就是前面第一个getSingleton()方法中所使用到的singletonFactories属性,也就是说,这里就是
// 封装半成品的bean的地方。而这里的getEarlyBeanReference()本质上是直接将放入的第三个参数,也就是
// 目标bean直接返回
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
try {
// 在初始化实例之后,这里就是判断当前bean是否依赖了其他的bean,如果依赖了,
// 就会递归的调用getBean()方法尝试获取目标bean
populateBean(beanName, mbd, instanceWrapper);
} catch (Throwable ex) {
// 省略...
}
return exposedObject;
}