spring关于单例模式循环依赖的处理

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值