- 👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主
- 📕系列专栏:Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙、Spring从成神到升仙系列
- 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
- 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
- 📝联系方式:hls1793929520,加我进群,大家一起学习,一起进步👀
对于Java开发者而言,关于 Spring
,我们一般当做黑盒来进行使用,不需要去打开这个黑盒。
但随着目前程序员行业的发展,我们有必要打开这个黑盒,去探索其中的奥妙。
本期 Spring
源码解析系列文章,将带你领略 Spring
源码的奥秘
本期源码文章吸收了之前 Kafka
源码文章的错误,将不再一行一行的带大家分析源码,我们将一些不重要的部分当做黑盒处理,以便我们更快、更有效的阅读源码。
废话不多说,发车!
本文流程图可关注公众号:爱敲代码的小黄,回复:循环依赖 获取
贴心的小黄为大家准备的文件格式为 POS文件,方便大家直接导入 ProcessOn 修改使用
我们上几篇文章讲解了 IOC、AOP的源码实现,如果没有看过的同学可以去看一下:
如果上面的文章你已经熟悉了,那么对于循环依赖的理解就会变得很简单,甚至你自己都能够想明白整个运行原理
我们首先介绍一下循环依赖的场景
我们在委托 Spring
进行对象的创建时,会遇到下面的情况:
MyDemo1:
public class MyDemo1 {public MyDemo2 myDemo2;public MyDemo1(MyDemo2 myDemo2) {this.myDemo2 = myDemo2;}
}
MyDemo2:
public class MyDemo2 {public MyDemo1 myDemo1;public MyDemo2(MyDemo1 myDemo1) {this.myDemo1 = myDemo1;}
}
xml文件配置:
测试用例:
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?
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配置:
测试用例:
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
到这里,有没有一点点惊讶、一点点懵逼、一点点卧槽
如果有的话,那这篇文章将带你解析为什么两种方式不同的注入方式
一种可能正常运行,一种不能正常运行
这里我们搬出 IOC
源码中的流程图:
我们分别聊一下有参构造场景下和有参注入场景下的不同
我们通过上图看到,如果一个类需要通过有参构造创建实例化,那么需要得到其构造方法的入参:
整体情况如上所示,我们总是重复性的循环,MyDemo1
的实例化创建依赖 MyDemo2
,而 MyDemo2
的实例化创建又需要依赖 MyDemo1
,这样就导致了死循环并无法解决。
所以,当我们的 Spring
察觉到有参构造导致的循环依赖时,会进行报错,这种的循环依赖也是没有办法解决的。
大家看这张图,可能会疑惑,这不也造成了循环依赖嘛,怎么这种方式没报错
我们想想这种属性注入导致的循环依赖能不能靠其他的方式去解决,加缓存可不可以
我们来看这种解决方式:
MyDemo1
调用无参构造生成实例(不是完全的实例)时,将其放至我们的缓存池中MyDemo1
调用属性注入时,会去缓存池中寻找 MyDemo2
的实例,若找不到的话,则调用 CreateBean
方法创建 MyDemo2
的实例MyDemo2
调用无参构造生成实例(不是完全的实例)时,将其放至我们的缓存池中MyDemo2
调用属性注入时,会去缓存池中寻找 MyDemo1
的实例,找到之后之前,执行后续的方法生成对应的实例化MyDemo1
已经得到了 MyDemo2
的实例化数据了,直接执行初始方法创建实例即可通过上述这种方式,我们已经将 属性注入
的循环依赖问题用加一层缓存的方式解决掉了
而这个缓存也被我们称作 提前暴露(earlySingletonObjects) 的缓存
我们上面可以看到,我们用一层 提前暴露(earlySingletonObjects) 的缓存解决了属性注入导致的循环依赖问题
这时候你可能会说:小黄,小黄,不是三级缓存嘛,你这咋就讲了一个 提前暴露(earlySingletonObjects) 缓存
不要着急,我们继续往下讲
假如我们现在 MyDemo1
被 AOP
动态代理,如果我们再按照上面的方式去进行缓存,会造成什么结果?
我们 MyDemo2 中的成员变量 MyDemo1 是未经动态代理的,这样使用 MyDemo1 时,实际上也是非动态代理的对象,这样是不被允许的!
为什么会有上面的问题呢?
根本原因在于:我们的属性注入的阶段在我们的执行初始方法(AOP)之前,缓存池中的半实例化对象不是我们代理对象
那怎么解决这个问题呢——没错,还是加缓存
我们再加一层缓存,该缓存的作用:如果我们半实例化的对象是代理对象,那么我们得到其代理对象
如上所示,整体的业务如上,我们详细的聊一聊 Spring
源码对于循环依赖的处理
我们以属性注入的例子来进行源码解析:
在我们讲解之前,我介绍一下三级缓存各自的功能:
这三个缓存非常重要,必须要记住。
当我们使用 ApplicationContext context = new GenericXmlApplicationContext("application.xml");
启动时,会进行我们 Bean
的创建
这里只说最关键的步骤,整体的步骤可见:Spring IOC 源码剖析
整体流程如下:
此时的缓存:
我们直接跳到这里:AbstractBeanFactory
的 246
行
protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly){// Step1:查询MyDemo1缓存是否存在Object sharedInstance = getSingleton(beanName);// 如果是单例的beanif (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;
}
我们直接跳到 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;
}
此时的缓存:
我们直接跳到这里:AbstractBeanFactory
的 246
行
protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly){// Step4:查询MyDemo2缓存是否存在Object sharedInstance = getSingleton(beanName);// 如果是单例的beanif (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {return createBean(beanName, mbd, args);});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
}
我们直接跳到 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));}
}
此时的缓存:
我们直接跳到这里:AbstractBeanFactory
的 246
行
protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly){// Step6:从缓存中获取 MyDemo1 Object sharedInstance = getSingleton(beanName);// 如果是单例的beanif (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {return createBean(beanName, mbd, args);});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
}
protected T doGetBean(String name, @Nullable Class 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);
}
到这里,我们的缓存的状态如下:
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);}
}
此时各缓存情况:
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);}
}
此时各缓存情况:
到这里,我们的循环依赖的整体流程就被解决了
又是一篇大工程的文章结束了
记得校招时候,当时对 Spring
懵懂无知,转眼间也被迫看了源码
更可怕的是,现在面试竟然百分之80都要熟悉IOC
、AOP
的源码,甚至手写 AOP
的实现
但通过这篇文章,我相信,99% 的人应该都可以理解了 Spring 循环依赖
的实现
那么如何证明你真的理解了 Spring 循环依赖
呢,我这里出个经典的题目,大家可以想一下:为什么Spring要用三级缓存,二级不可以嘛?
如果你能看到这,那博主必须要给你一个大大的鼓励,谢谢你的支持!
喜欢的可以点个关注,Spring
系列到此正式结束了~
后续博主应该会更新 dubbo 或者 并发编程 的系列文章,
我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,Java领域新星创作者,喜欢后端架构和中间件源码。
我们下期再见。