我们上几篇文章讲解了 IOC、AOP的源码实现,如果没有看过的同学可以去看一下:
Spring IOC 源码剖析
Spring AOP 源码剖析
如果上面的文章你已经熟悉了,那么对于循环依赖的理解就会变得很简单,甚至你自己都能够想明白整个运行原理
我们首先介绍一下循环依赖的场景
我们在委托 Spring 进行对象的创建时,会遇到下面的情况:
1、有参构造引起的循环依赖
MyDemo1:
public class MyDemo1 {
public MyDemo2 myDemo2;
public MyDemo1(MyDemo2 myDemo2) {
this.myDemo2 = myDemo2;
}
}
1
2
3
4
5
6
MyDemo2:
public class MyDemo2 {
public MyDemo1 myDemo1;
public MyDemo2(MyDemo1 myDemo1) {
this.myDemo1 = myDemo1;
}
}
1
2
3
4
5
6
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDemo1" class="cn.hls.demo1.MyDemo1">
<constructor-arg value="myDemo2"/>
</bean>
<bean id="myDemo2" class="cn.hls.demo1.MyDemo2">
<constructor-arg value="myDemo1"/>
</bean>
</beans>
测试用例:
public class TestMain {
public static void main(String[] args) {
ApplicationContext context = new GenericXmlApplicationContext("application.xml");
MyDemo1 myDemo1 = (MyDemo1) context.getBean("myDemo1");
myDemo1.show();
}
}
运行,不出所料,我们会报错:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'myDemo1': Requested bean is currently in creation: Is there an unresolvable circular reference?
1
2、属性注入引起的循环依赖
MyDemo1:
public class MyDemo1 {
public MyDemo2 myDemo2;
public void show() {
System.out.println("我是" + MyDemo1.class.getName());
}
public void setMyDemo2(MyDemo2 myDemo2) {
this.myDemo2 = myDemo2;
}
public MyDemo2 getMyDemo2() {
return myDemo2;
}
}
MyDemo2:
public class MyDemo2 {
public MyDemo1 myDemo1;
public void show() {
System.out.println("我是" + MyDemo2.class.getName());
}
public MyDemo1 getMyDemo1() {
return myDemo1;
}
public void setMyDemo1(MyDemo1 myDemo1) {
this.myDemo1 = myDemo1;
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDemo1" class="cn.hls.demo1.MyDemo1">
<property name="myDemo2" ref="myDemo2"/>
</bean>
<bean id="myDemo2" class="cn.hls.demo1.MyDemo2">
<property name="myDemo1" ref="myDemo1"/>
</bean>
</beans>
测试用例:
public class TestMain {
public static void main(String[] args) {
ApplicationContext context = new GenericXmlApplicationContext("application.xml");
MyDemo1 myDemo1 = (MyDemo1) context.getBean("myDemo1");
MyDemo2 myDemo2 = (MyDemo2) context.getBean("myDemo2");
myDemo1.show();
myDemo2.show();
}
}
运行,我们竟然发现,这种是可以正常执行的
我是cn.hls.demo1.MyDemo1
我是cn.hls.demo1.MyDemo2
1
2
到这里,有没有一点点惊讶、一点点懵逼、一点点卧槽
如果有的话,那这篇文章将带你解析为什么两种方式不同的注入方式
一种可能正常运行,一种不能正常运行
三、循环依赖的原因
这里我们搬出 IOC 源码中的流程图:
我们分别聊一下有参构造场景下和有参注入场景下的不同
1、有参构造失败的原因
我们通过上图看到,如果一个类需要通过有参构造创建实例化,那么需要得到其构造方法的入参:
整体情况如上所示,我们总是重复性的循环,MyDemo1 的实例化创建依赖 MyDemo2,而 MyDemo2 的实例化创建又需要依赖 MyDemo1,这样就导致了死循环并无法解决。
所以,当我们的 Spring 察觉到有参构造导致的循环依赖时,会进行报错,这种的循环依赖也是没有办法解决的。
2、属性注入成功的原因
大家看这张图,可能会疑惑,这不也造成了循环依赖嘛,怎么这种方式没报错
我们想想这种属性注入导致的循环依赖能不能靠其他的方式去解决,加缓存可不可以
我们来看这种解决方式:
我们 MyDemo1 调用无参构造生成实例(不是完全的实例)时,将其放至我们的缓存池中
MyDemo1 调用属性注入时,会去缓存池中寻找 MyDemo2 的实例,若找不到的话,则调用 CreateBean 方法创建 MyDemo2 的实例
MyDemo2 调用无参构造生成实例(不是完全的实例)时,将其放至我们的缓存池中
MyDemo2 调用属性注入时,会去缓存池中寻找 MyDemo1 的实例,找到之后之前,执行后续的方法生成对应的实例化
这个时候我们的 MyDemo1 已经得到了 MyDemo2 的实例化数据了,直接执行初始方法创建实例即可
通过上述这种方式,我们已经将 属性注入 的循环依赖问题用加一层缓存的方式解决掉了
而这个缓存也被我们称作 提前暴露(earlySingletonObjects) 的缓存
2.1 AOP导致的循环依赖
我们上面可以看到,我们用一层 提前暴露(earlySingletonObjects) 的缓存解决了属性注入导致的循环依赖问题
这时候你可能会说:小黄,小黄,不是三级缓存嘛,你这咋就讲了一个 提前暴露(earlySingletonObjects) 缓存
不要着急,我们继续往下讲
假如我们现在 MyDemo1 被 AOP 动态代理,如果我们再按照上面的方式去进行缓存,会造成什么结果?
我们 MyDemo2 中的成员变量 MyDemo1 是未经动态代理的,这样使用 MyDemo1 时,实际上也是非动态代理的对象,这样是不被允许的!
为什么会有上面的问题呢?
根本原因在于:我们的属性注入的阶段在我们的执行初始方法(AOP)之前,缓存池中的半实例化对象不是我们代理对象
那怎么解决这个问题呢——没错,还是加缓存
我们再加一层缓存,该缓存的作用:如果我们半实例化的对象是代理对象,那么我们得到其代理对象
如上所示,整体的业务如上,我们详细的聊一聊 Spring 源码对于循环依赖的处理
四、循环依赖 Spring 源码剖析
我们以属性注入的例子来进行源码解析:
在我们讲解之前,我介绍一下三级缓存各自的功能:
一级缓存(singletonObject):存储的是所有创建好了的单例Bean
二级缓存(earlySingletonObjects):完成实例化,但是还未进行属性注入及初始化的对象
三级缓存(singletonFactories):提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
这三个缓存非常重要,必须要记住。
当我们使用 ApplicationContext context = new GenericXmlApplicationContext("application.xml"); 启动时,会进行我们 Bean 的创建
这里只说最关键的步骤,整体的步骤可见:Spring IOC 源码剖析
整体流程如下:
步骤一:查询 MyDemo1 是否存在
此时的缓存:
我们直接跳到这里:AbstractBeanFactory 的 246 行
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
// Step1:查询MyDemo1缓存是否存在
Object sharedInstance = getSingleton(beanName);
// 如果是单例的bean
if (mbd.isSingleton()) {
// 直接创建bean即可,注意 getSingleton 方法
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
// Step1:从三级缓存中查询 MyDemo1 是否被缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 一级缓存查询
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 二级缓存查询
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 三级缓存查询
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
// 这里记住一个操作:在我们创建bean结束之后,会调用 addSingleton 该方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
步骤二:将 MyDemo1 半实例化放至缓存中
我们直接跳到 AbstractAutowireCapableBeanFactory 的 580 行
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
// 是否需要提前暴露
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 如果需要提前暴露,则放入到我们的三级缓存里面
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
}
// 将未完全实例化的 MyDemo1 放至缓存中
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 三级缓存
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
// 这个主要是记录当前注册的对象(不太重要)
this.registeredSingletons.add(beanName);
}
}
}
// 这个是重点:生成动态代理对象的地方,我们后面会讲
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;
}
此时的缓存:
步骤三、四:查询 MyDemo2 的缓存是否存在
我们直接跳到这里:AbstractBeanFactory 的 246 行
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
// Step4:查询MyDemo2缓存是否存在
Object sharedInstance = getSingleton(beanName);
// 如果是单例的bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
步骤五:将 MyDemo2 半实例化放至缓存中
我们直接跳到 AbstractAutowireCapableBeanFactory 的 580 行
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
// 是否需要提前暴露
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 如果需要提前暴露,则放入到我们的三级缓存里面
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
}
此时的缓存:
步骤六:从缓存中获取 MyDemo1
我们直接跳到这里:AbstractBeanFactory 的 246 行
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
// Step6:从缓存中获取 MyDemo1
Object sharedInstance = getSingleton(beanName);
// 如果是单例的bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) {
// 这里获取的是 MyDemo1 的缓存,我们之前已经放入过
Object sharedInstance = getSingleton(beanName);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 【重点】从三级缓存中取到
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 调用 getEarlyBeanReference 的方法生成对象
singletonObject = singletonFactory.getObject();
// 将生成的半实例对象放至二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除掉三级缓存的信息
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
我们来看一下 getEarlyBeanReference 做了什么、
如果是普通的类,没有被动态代理的,直接返回 bean 即可
如果是动态代理的类,需要进行动态代理类的生成并返回
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;
}
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 这里会生成动态代理类【AOP文章讲过】
return wrapIfNecessary(bean, beanName, cacheKey);
}
到这里,我们的缓存的状态如下:
步骤七:将 MyDemo2 生成的实例化放至 singletonObject 中
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
// 当bean初始化完成之后
// 删除二级缓存、三级缓存,将其放入一级缓存中
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
此时各缓存情况:
步骤八:将 MyDemo1 生成的实例化放至 singletonObject 中
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
// 当bean初始化完成之后
// 删除二级缓存、三级缓存,将其放入一级缓存中
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
此时各缓存情况:
到这里,我们的循环依赖的整体流程就被解决了
免责申明:
本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!
《数据治理行业实践白皮书》下载地址:https://fs80.cn/4w2atu
《数栈V6.0产品白皮书》下载地址:https://fs80.cn/cw0iw1
想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs
同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:https://github.com/DTStack