上篇文章我们说了SpringBean的生命周期,知道了SpringBean的实例化创建和销毁的过程,但是我们知道Spring是支持一个Bean可以引入另外的Bean对象的,那么就不可避免出现相互依赖的问题,Spring是如何解决这个问题呢?Spring注册Bean的方式有很多种,Spring又是可以解决哪些循环依赖又不能解决哪几种循环依赖呢,本文会一一介绍。
一、什么是循环依赖
通俗的讲,循环依赖是指Spring中N个Bean相互依赖从而形成一个闭环的现象。
相互依赖只是闭环的因,闭环是相互依赖的果,特殊情况下自己依赖自己也是一种闭环。
二、循环依赖的代码演示
2.1、引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sxx</groupId>
<artifactId>CyclicDependence</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.1.6.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<slf4j.version>1.7.35</slf4j.version>
<aspectjweaver.version>1.8.9</aspectjweaver.version>
<json.version>1.2.27</json.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${json.version}</version>
</dependency>
</dependencies>
</project>
2.2、创建对象
package com.sxx.cyclic.entity;
import com.alibaba.fastjson.JSON;
public class A {
private String name;
private B b;
@Override
public String toString() {
// System.out.println("A中注入了Bean对象" + b);
return JSON.toJSONString(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
package com.sxx.cyclic.entity;
import com.alibaba.fastjson.JSON;
public class B {
private Integer age;
private A a;
@Override
public String toString() {
// System.out.println("B中注入了Bean对象" + a);
return JSON.toJSONString(this);
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
2.3、编写xml文件
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="a" class="com.sxx.cyclic.entity.A">
<property name="name" value="张三"/>
<!--引入B-->
<property name="b" ref="b"/>
</bean>
<bean id="b" class="com.sxx.cyclic.entity.B">
<property name="age" value="20"/>
<!--引入A-->
<property name="a" ref="a"/>
</bean>
</beans>
2.4、测试类
package com.sxx.cyclic.entity;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CyclicTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("classpath*:bean.xml");
A a = applicationContext.getBean("a", A.class);
System.out.println(a.toString());
}
}
2.5、结果
{"b":{"a":{"$ref":".."},"age":20},"name":"张三"}
三、Spring的循环依赖
从上面的代码我们可以很明显发现两个问题:
1.我在重写A、B对象的toString方法时把引入的其他bean对象打印出来了,但是在实际执行的时候我却把他注释掉了;
2.在调用A的toString方法时,打印出来的a对象里面出现了本不该出现的属性,如下图所示:
试想下为什么会出现上述两种情况?
首先我们可以尝试放开toString中的打印语句看看会出现什么情况。
可以看到,当我们放开A、B中toString的打印语句后出现StackOverflowError,这说明我们的代码中出现了循环调用,根据之前的代码很容易猜测是出现了A、B两个bean相互持有的缘故,那么这又是在什么时候触发的呢?我们可以先注释掉toString打印信息,然后在getBean方法之后查看下A、B两个bean的对象信息。
从图中可以看出,在Spring启动之后我们通过getBean从容器中获取A对象的时候,此时的A、B两个bean还在互相依赖,出现了闭环,于是在后续调用toString的时候要依赖打印b对象,b对象中又依赖打印a对象,于是出现了StackOverflowError,这也能解释为什么会出现下面这种现象了。
{"b":{"a":{"$ref":".."},"age":20},"name":"张三"}
3.1、Spring如何解决循环依赖
先放出流程图,后面会从源码层面一步步解析
3.2、Spring创建singleton bean过程
要了解Spring解决循环依赖的过程,我们首先要知道Spring创建singleton bean的过程。
3.2.1、三级缓存
要了解singleton bean创建过程首先要知道三级缓存的概念,关于Spring的三级缓存在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry下,其源码如下:
/**
* 一级缓存:单例(对象)池,这里面的对象都是确保初始化完成,可以被正常使用的
* 它可能来自3级,或者2级
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
/**
* 三级缓存:单例工厂池,这里面不是bean本身,是它的一个工厂,未来调getObject来获取真正的bean
* 一旦获取,就从这里删掉,进入2级(发生闭环的话)或1级(没有闭环)
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
/**
* 二级缓存:早期(对象)单例池,这里面都是半成品,只是有人用它提前从3级get出来,把引用暴露出去
* 它里面的属性可能是null,所以叫早期对象,early:半成品
* 未来在getBean付完属性后,会调addSingleton清掉2级,正式进入1级
*/
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
注意:三级缓存其实质就是三个不同功能的map,严格意义上来说只有singletonObjects算是一级缓存,其他两个只是Spring在生成bean过程中起辅助作用,即解决生成bean过程中存在的一些问题的。
3.2.2、Spring创建singleton bean过程
我们知道在Spring初始化之后,我们需要通过getBean()去拿到对应的Bean对象,那么getBean里面都做了哪些操作呢?
从上图可知,getBean方法是调用了doGetBean()方法,这个才是getBean的实际执行内容,我们看到下面对相关源码的分析:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
//转化beanname,是否&开头,skip
String beanName = this.transformedBeanName(name);
//先尝试获取如果拿不到再创建,循环依赖闭环可以拿到;拿1级-2级-3级
Object sharedInstance = this.getSingleton(beanName);
Object bean;
//判断是否是闭环,是的时候进入
if (sharedInstance != null && args == null) {
if (this.logger.isTraceEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//下面这个方法:如果是普通 Bean 的话,直接返回 sharedInstance
//如果是 FactoryBean 的话,返回它创建的那个实例对象
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
//没有创建过原型类型的bean
if (this.isPrototypeCurrentlyInCreation(beanName)) {
//当前线程已经创建过了此 beanName 的 prototype 类型的 bean,那么抛异常
throw new BeanCurrentlyInCreationException(beanName);
}
//检查一下这个 BeanDefinition 在容器中是否存在(初始化不存在)
BeanFactory parentBeanFactory = this.getParentBeanFactory();
if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
//如果当前容器不存在这个 BeanDefinition,试试父容器中有没有
String nameToLookup = this.originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
}
if (args != null) {
//返回父容器的查询结果
return parentBeanFactory.getBean(nameToLookup, args);
}
if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
return parentBeanFactory.getBean(nameToLookup);
}
//typeCheckOnly 为 false,将当前 beanName 放入一个 alreadyCreated 的 Set 集合中
if (!typeCheckOnly) {
//将当前的bean存入到set集合alreadyCreated中
this.markBeanAsCreated(beanName);
}
try {
//beanDefinitionMap.get()
RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
//仅仅检查这个RootBeanDefinition是否为抽象的
this.checkMergedBeanDefinition(mbd, beanName, args);
//先初始化有depends-on依赖的所有Bean
String[] dependsOn = mbd.getDependsOn();
String[] var11;
if (dependsOn != null) {
var11 = dependsOn;
int var12 = dependsOn.length;
for(int var13 = 0; var13 < var12; ++var13) {
String dep = var11[var13];
//检查是不是有循环依赖
if (this.isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
//注册一下依赖关系
this.registerDependentBean(dep, beanName);
try {
//初始化被依赖项
this.getBean(dep);
} catch (NoSuchBeanDefinitionException var24) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);
}
}
}
//创建 singleton 的实例
if (mbd.isSingleton()) {
//bean实例化完后放到一级缓存
sharedInstance = this.getSingleton(beanName, () -> {
try {
//执行创建bean
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
//显示从单例缓存中删除bean,并删除该bean临时引用的所有bean
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
//创建prototype 的实例
} else if (mbd.isPrototype()) {
var11 = null;
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
//执行创建bean
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
// 如果不是singleton或prototype的话,需要委托给相应的实现类来处理
} else {
String scopeName = mbd.getScope();
Scope scope = (Scope)this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
this.beforePrototypeCreation(beanName);
Object var4;
try {
var4 = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
return var4;
});
bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException var23) {
throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23);
}
}
} catch (BeansException var26) {
this.cleanupAfterBeanCreationFailure(beanName);
throw var26;
}
}
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
} else {
return convertedBean;
}
} catch (TypeMismatchException var25) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
} else {
return bean;
}
}
具体的主流程如下图所示:
这里以Spring5.1.6版本为例,各个方法的入口如下:
1、getBean的入口:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
2、根据条件尝试从三级缓存中拿到Bean的入口org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
3、这个和2名字相同,但是作用完全不一样,他是通过factory创建org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
4、执行创建Bean的入口org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
5、实际创建Bean的方法org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
6、实例化Bean的入口org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
7.真正填充Bean属性的入口
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
8、调用Bean的后置处理器的方法入口
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
3.2.3、三个getSingleton
从上面可以看出在创建单例bean的时候出现了两个同名不同作用的getSingleton方法,实际上在DefaultSingletonBeanRegistry中重载了三个getSingleton方法,他们的作用也是不相同的。
@Nullable
public Object getSingleton(String beanName) {
return this.getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先从一级缓存中拿
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
//在从二级缓存拿
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference是否允许循环依赖
if (singletonObject == null && allowEarlyReference) {
//在从三级缓存拿,如果找到则在这结束这个死循环
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//调用早期对象方法AbstractAutowireCapableBeanFactory.getEarlyBeanReference
singletonObject = singletonFactory.getObject();
//三级缓存返回的早期对象放到了二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//移除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
//返回这个单例对象
return singletonObject;
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized(this.singletonObjects) {
//尝试从一级缓存中拿
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//判断是否是正在销毁的单例
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
//创建前的检查
this.beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = this.suppressedExceptions == null;
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet();
}
try {
//调用createBean方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException var16) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw var16;
}
} catch (BeanCreationException var17) {
BeanCreationException ex = var17;
if (recordSuppressedExceptions) {
Iterator var8 = this.suppressedExceptions.iterator();
while(var8.hasNext()) {
Exception suppressedException = (Exception)var8.next();
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
this.afterSingletonCreation(beanName);
}
if (newSingleton) {
//bean实例化完后 ,放到一级缓存
this.addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
根据getSingleton的源码可知,三个方法的的作用如下:
第一个getSingleton:调用第二个getSingleton,同时传入beanName和true;
第二个getSingleton:先从一级缓存中拿bean,如果没有再校验条件,同时根据allowEarlyReference判断是否进入下面方法,如果进入就从三级拿bean,同时三级缓存移入二级缓存,否则直接返回null;
第三个getSingleton:先从一级缓存中拿bean,如果没有,通过传入的singletonFactory创建并放入一级缓存中,同时清除二三级缓存。
3.3、Spring解决循环依赖的过程
其实从之前的Spring解决循环依赖的流程图与Spring创建singleton bean的源码分析我们很容易知道,Spring处理循环依赖的流程如下:
- 入口为getBean("a")方法;
- 进入doGetBean()方法开始创建A对象;
- 第一遍先去一级缓存中查询,因为Spring刚启动,第一遍肯定查询不到;
- 开始在doGetBean中进行A的初始化,并放入三级缓存中;
- A初始化完成后开始填充属性,在填充属性的时候发现需要注入B对象,因此触发getBean("b");
- 重复1-4的流程,然后开始对B填充属性,再次进入到getBean("a")方法中;
- 此时不在和第一次进入getBean("a")一样了,根据条件检测有闭环,开始尝试从三级缓存中拿到一个半成品即没有填充属性的A对象,这事便可以完成对B对象的填充;
- B对象初始化完成后,将B移入到一级缓存中;
- 此时回转到第一次getBean("a")时便可以获取到一个成品的B对象,此时便完成了A对象的填充
- 至此,A、B循环依赖解决完成。
至此能解释在调用toString()方法时打印出来的对象A中B属性存在$ref的问题了
四、Spring循环依赖的若干问题
4.1、Spring不能解决的循环依赖
从上面的流程我们知道,Spring利用三级缓存的变化来解决循环依赖的,如果bean时多例的话每次都需要去实例化一个新的对象,压根就不用三级缓存,因此无法解决多例的缓存依赖。除此之前,我们知道Spring是在A实例化完成之后,在填充属性时才开始getBean("b")触发b的实例化过程,而如果是通过构造方法进行的循环依赖,实例化时需要调用构造方法,也没有办法触发b的实例化过程,因此也无法解决此种情况的循环依赖。
前提 | 依赖注入的方式 | 是否可以解决循环依赖 | 原因 |
A、B互相依赖(循环依赖) | 均采用setter注入 | 是 | 三级缓存可以解决循环依赖 |
A、B互相依赖(循环依赖) | 均采用构造器注入 | 否 | A实例化的时候要调用构造器,需要引入B,此时未到填充A属性过程,不能触发B的实例化过程 |
A、B互相依赖(循环依赖) | A注入B的方式为setter,B注入A的方式为构造器 | 是 | A在填充属性的时候可以触发B实例化过程,B在实例化时可以在三级缓存中拿到A的代理bean,因此B可以正常初始化 |
A、B互相依赖(循环依赖) | A注入B的方式为构造器,B注入A的方式为setter | 否 | A实例化的时候要调用构造器,需要引入B,此时未到填充A属性过程,不能触发B的实例化过程 |
4.2、为什么要用三级缓存解决循环依赖
我们已经知道了Spring利用三级缓存来解决循环依赖了,为什么三级缓存?一级或者二级缓存能不能解决循环依赖呢?
从单纯的解决循环依赖的角度上来看,二级甚至是一级缓存都是可以处理缓存依赖的,那么Spring为什么要用三级缓存缓存呢?
首先说下一级缓存的问题。
我们知道Spring的bean有的是需要aop进行增强的,对于这样的bean最终放到缓存中的应该是一个代理 bean。而代理 bean 的产生是在 initializeBean(第三阶段) 的时候。所以,我们推导出:如果只使用一级缓存的话,缓存的插入应该放在 initializeBean 之后。
如果在 initializeBean 的时候记录缓存,那么碰到循环依赖的情况,需要在 populateBean(第二阶段) 的时候再去注入循环依赖的 bean,此时,缓存中是没有循环依赖的 bean 的,就会导致 bean 重新创建实例,这样显然是不行的。
那么可以用二级缓存解决循环依赖吗?
答案是可以的,那么Spring为什么不用二级缓存呢?前面我们已经详细介绍了Spring三级缓存的不同作用,我们知道,Spring的二级缓存放的是半成品的bean,没有过多的扩展,如果仅仅用于解决循环依赖是完全可以的,但是三级缓存是一个Factory,里面可以在创建的前后嵌入我们的代码,和前后置处理器,Aop(getObject中)之类的操作就发生在这里,他的扩展性比二级缓存强,看起来也更加清晰明白,这就是三级缓存的绝妙之处。