1:写在前面
本文在spring通过不同的方式创建bean的基础上进行分析,作为补充,详细分析spring是如何解决单例模式下的循环依赖的,关于什么是循环依赖可以参考这里,由于spring只会解决单例模式下基于属性引用而构成的循环依赖,因此本文分析也是这种方式的循环依赖(画外音:其他的也没啥好分析的,因为直接就异常了!
)。
2:准备工作
2.1:准备2个类
public class MyA {
private MyB myB;
public MyB getMyB() {
return myB;
}
public void setMyB(MyB myB) {
this.myB = myB;
}
}
public class MyB {
private MyA myA;
public MyA getMyA() {
return myA;
}
public void setMyA(MyA myA) {
this.myA = myA;
}
}
2.2: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:ok="http://dongshi.daddy.com/schema/ok"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://dongshi.daddy.com/schema/ok
http://dongshi.daddy.com/schema/ok/dongshidaddy-1.0.xsd">
<bean class="yudaosourcecode.singletonrecycleref.MyA"
id="myA"
lazy-init="true"
scope="singleton">
<property name="myB" ref="myB"/>
</bean>
<bean class="yudaosourcecode.singletonrecycleref.MyB"
id="myB"
lazy-init="true"
scope="singleton">
<property name="myA" ref="myA"/>
</bean>
</beans>
注意其中的scope=singleton
,;lazy-init=true
,设置懒加载的目的是在获取bean的时候才去初始化,方便我们进行调试。
2.3:测试代码
@Test
public void testsingletonrecycleref() {
ClassPathXmlApplicationContext ac
= new ClassPathXmlApplicationContext("testsingletonrecycleref.xml");
ac.getBean("myA");
}
3:getSingleton
位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)
在测试代码ac.getBean("myA");
处打断点调试。
3.1:如何调试
debug模式运行测试程序,调试执行到代码org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
,如下:
此时,if判断的结果如下:
此时要加载的是myA
,然后F8
会因为需要加载myB
会再次进入,此时if判断依然为false,因为此时正在创建的单例bean名称只有myA
,如下图:
然后再次F8
此时因为又要加载myA
会再次执行到这里,此时正在创建的单例bean的名称集合中有myA,myB
两个了,当前要加载myA,而myA已经在创建中了,就构成循环依赖了,如下:
如果是需要调试程序的话,就可以在这个执行状态下调试了。
3.2:源码
源码如下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// <2021-03-25 16:19>
Object singletonObject = this.singletonObjects.get(beanName);
// <2021-03-25 16:20>
// 如果是存在循环依赖
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 对singletonObjects上锁
synchronized (this.singletonObjects) {
// 先从earlySingletonObjects中根据beanName获取
// 获取,这是一个map,key为beanName,value为半成品
// 的bean实例,作为解决循环依赖的二级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果是earlySingletonObjects中没有,且允许通过
// ObjectFacotry的getObject方法获取bean实例
if (singletonObject == null && allowEarlyReference) {
// 从singletonFactories中获取,这是一个map
// key是beanName,value是ObjectFactory,通过其getObject方法可以
// 获取具体的bean实例,该map作为解决循环依赖的
// 三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 如果是singleFactory不为null
if (singletonFactory != null) {
// <2021-03-26 11:39>
// 调用其getObject方法获取bean
singletonObject = singletonFactory.getObject();
// 将从singleFactories#getObject中获取的bean添加到
// 存储半成品bean的map即二级缓存earlySingletonObjects中
this.earlySingletonObjects.put(beanName, singletonObject);
// 此时半成品bean已经存储到二级缓存map earlySingletonObjects中
// 了,三级缓存singletonFactories中就没有用了
// 因此从其中删除
this.singletonFactories.remove(beanName);
}
}
}
}
// 返回单例bean
return singletonObject;
}
因为比较重要,所以在开始分析之前我们先来看下该程序的执行流程图:
这三级缓存的作用如下:
缓存级别 | 缓存变量名 | 作用 |
---|---|---|
一级缓存 | singletonObjects | 存储最终的单例bean |
二级缓存 | earlySingletonObjects | 提前曝光单例bean |
三级缓存 | singletonFactories | 存储创建单例bean的工厂,处理存在需要代理等情况 |
<2021-03-25 16:19>
处参考3.2.1:从一级缓存中获取单例bean
,<2021-03-25 16:20>
处就是判断是否存在循环依赖的情况,如果是没有从单例缓存中获取,并且是当前beanName还正在创建中,此时就说明存在循环依赖了,判断正在创建方法isSingletonCurrentlyInCreation
如下:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#isSingletonCurrentlyInCreation
public boolean isSingletonCurrentlyInCreation(String beanName) {
// singletonsCurrentlyInCreation是存储正在创建的单例bean名称
// 的集合,如果是包含则说明在创建中
return this.singletonsCurrentlyInCreation.contains(beanName);
}
<2021-03-26 11:39>
处是从三级缓存singletonFactories中获取单例bean对应的ObjectFactory,具体参考3.2.2:从三级缓存中获取bean
。
3.2.1:从一级缓存中获取单例bean
singletonObjects
定义为private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
其中的key是bean名称
,value是最终的可用的bean实例
,这个也是spring解决循环依赖的3级缓存中的第1级缓存
,所以这里就是简单的通过bean名称来从一个map中获取一个bean,那么bean是在什么时候被put进来的呢,是在方法org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
中,我们先看下下面的初始化流程:
其中的红色背景就是执行这里的代码,大家可以按照这个流程来debug测试,源码如下:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加到一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 从三级缓存中删除,其实此时应该已经删除过了,但是保险起见,在执行一次
this.singletonFactories.remove(beanName);
// 从二级缓存中删除
this.earlySingletonObjects.remove(beanName);
// 添加到已注册的bean名称集合中
this.registeredSingletons.add(beanName);
}
}
3.2.2:从三级缓存中获取bean
变量singleFactories
定义为private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
,这是spring解决循环依赖所使用的三级缓存,其中的ObjectFactory是一个函数式接口,定义如下:
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
只有一个方法getObject
用来返回内部创建的bean,因此该接口的作用就是创建一个bean,既然能从这里来获取,那么一定是有一个地方将信息放进来的,具体是在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
执行到这里的调用的核心方法名称大概为getBean->doGetBean->createBean->doCreateBean
,源码如下:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
...snip...
// 满足以下条件则则允许提前曝光bean
// 1:是单例的 2:允许循环引用 3:当前bean正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 如果是允许提前曝光的话
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// <2021-03-26 15:29>
// 添加到三级缓存singletonFactories中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
...snip...
return exposedObject;
}
addSingletonFactory
是添加ObjectFactory到三级缓存singletonFactories中,源码如下:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
// 使用解决循环依赖的一级缓存singletonObjects上锁
synchronized (this.singletonObjects) {
// 如果是还不在一级缓存singletonObjects中
if (!this.singletonObjects.containsKey(beanName)) {
// 添加到三级缓存singletonFactories中
this.singletonFactories.put(beanName, singletonFactory);
// 从二级缓存earlySingletonObjects中删除
this.earlySingletonObjects.remove(beanName);
// 添加到已注册的单例bean集合中
this.registeredSingletons.add(beanName);
}
}
}
其中singletongFacotry
方法是通过() -> getEarlyBeanReference(beanName, mbd, bean)
定义的,那么当调用其getObject
方法时就会通过方法getEarlyBeanReference
来生成需要提前曝光的单例bean,下面我们来看下这个方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
// 参数beanName:标准bean名称
// 参数mdb:要生成的bean原始的beandefinition
// 参数bean:早期半成品bean,可以认为仅仅只是调用了构造函数的bean,还没有进行填充属性和初始化等操作
// AOP的动态织入就是在这里完成的(重要!!!)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}