单例模式下的构造器循环依赖
单例模式下的构造器循环依赖和多例模式下的循环依赖大同小异。以下面的TestA,TestB为例。
public class TestA {
private TestB testB;
public TestA(TestB testB) {
this.testB = testB;
}
public TestB getTestB() {
return testB;
}
public void setTestB(TestB testB) {
this.testB = testB;
}
}
public class TestB {
private TestA testA;
public TestB(TestA testA) {
this.testA = testA;
}
public TestA getTestA() {
return testA;
}
public void setTestA(TestA testA) {
this.testA = testA;
}
}
先解释几个缓存的概念,便于后续的理解。
/** 单例缓存池,这里存储的bean是已经完成了实例化,属性注入,初始化等一系列操作的bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 单例对象工厂缓存,存储了每个beanName所在的生产工厂,在调用构造生产实例时一般会加入到这里来 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 存储的提前暴露的早期对象,即只是调用了构造函数生成的bean对象 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
因为在单例模式下,所有被Spring托管的bean只会存在一份,所以Spring在创建完成bean后会将当前bean加入到单例缓存池中,便于以后的存取。那么二级,三级缓存呢?到底是用来干嘛的呢?先看下面的这段代码,。调用getBean创建testA时,会进入到doGetBean方法进行testA的创建
//真正实现向IOC容器获取Bean的功能,也是触发依赖注入功能的地方
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
Object sharedInstance = getSingleton(beanName);
//IOC容器创建单例模式Bean实例对象
if (sharedInstance != null && args == null) {
//获取给定Bean的实例对象,主要是完成FactoryBean的相关处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
if (mbd.isSingleton()) {
//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
//创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//显式地从容器单例模式Bean缓存中清除实例对象
destroySingleton(beanName);
throw ex;
}
});
//获取给定Bean的实例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
return (T) bean;
}
第一行的getSingleton方法非常重要,让我们来仔细解读下。
//先从缓存中取是否已经有被创建过的单态类型的Bean
//对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从单例bean池中获取,也就是一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
//当前池中没有,并且当前bean正在创建当中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//从早期暴露的对象工厂中获取到的object,此处表示调用构造方法生成后但未完成属性赋值的对象,也就是三级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
//早期bean为空,且运行提前引用
if (singletonObject == null && allowEarlyReference) {
//获取提前暴露的对象工厂,对象在通过构造函数创建生成实例后与生成对象的工厂建立绑定关系,便于后续解决循环依赖,也就是三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果能提前获取到bean的对象工厂
if (singletonFactory != null) {
//可以从工厂的getObject方法中找到绑定的具体的bean,此时的bean暂未完成属性赋值
singletonObject = singletonFactory.getObject();
//缓存升级,提升到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
testA第一时间进来时,单例缓存池是没有这个对象的,肯定为null。接着进入if判断,这里有一个isSingletonCurrentlyInCreation方法,看一看它具体的功能。
/** 存储了当前正在创建的beanName */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
public boolean isSingletonCurrentlyInCreation(String beanName) {
//判断当前beanName是否正在创建当中
return this.singletonsCurrentlyInCreation.contains(beanName);
}
逻辑很简单,就是判断了下当前的testA是否正在创建中,很明显,此时还没开始创建。所以返回false。条件不成立,直接返回null。进入doGetBean方法的第二个分支。此时,又调用了一个getSingleton方法,但是这里调用的是重载的getSingleton方法。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
//从单例缓存池中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
......
//将当前的beanName加入正在创建的集合中
beforeSingletonCreation(beanName);
......
try {
//从对象工厂中获取具体的实例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}catch (IllegalStateException ex) {
......
}catch (BeanCreationException ex) {
......
}
finally {
//从正在创建的bean集合中移除
afterSingletonCreation(beanName);
}
}
}
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
首先会从单例缓存池中获取对象,此时为null,然后调用beforeSingletonCreation方法将当前的testA加入到正在创建的集合中。当执行到singletonFactory.getObject();这一行时,就会调用lamda中的createBean方法来创建testA对象。
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//创建Bean的入口
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
//真正创建Bean的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//...省略无关代码
//1.调用默认构造函数进行反射生成实例
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//2.Bean对象的初始化,依赖注入在此触发
populateBean(beanName, mbd, instanceWrapper);
//3.初始化Bean对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
//...省略无关代码
return exposedObject;
}
进入doCreateBean方法真正的创建testA。这个方法大概经历了以上三步,实例化,属性注入,初始化。在Spring中默认是调用类的无参构造来进行实例化的。由于此时是构造函数依赖,在调用createBeanInstance方法时,通过反射获取到TestA的构造函数依赖TestB,此时又会立马回去创建testB对象,前面的步骤跟testA一样。当调用createBeanInstance方法时,发现此时的构造函数依赖TestA,此时又会立马回去创建testA对象。调用getBean创建testA时,isSingletonCurrentlyInCreation方法会返回true。进入下一个if判断,可是所有的缓存池中均还未保存过testA对象,所以还是返回null。然后逻辑继续走,当走到第二个getSingleton方法中时,此时判断当前的bean是否在创建,如果在创建,直接抛出BeanCreationNotAllowedException异常。
if (this.singletonsCurrentlyInDestruction) {
//当前bean正在创建,直接抛异常
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!)");
}
像单例模式下的构造器依赖,同样会出现像多例模式中依赖的问题,如此循环往复,从而形成死循环。Spring也不知道如何来处理这种情况,索性直接抛出异常。
下一章节,我会继续来探讨最重要的单例模式下的set注入循环依赖。