建设网站后如何上线,wordpress表单提交插件,软件项目实施计划方案,网络站点推广的方法有哪些#x1f44f;作者简介#xff1a;大家好#xff0c;我是爱敲代码的小黄#xff0c;独角兽企业的Java开发工程师#xff0c;CSDN博客专家#xff0c;阿里云专家博主#x1f4d5;系列专栏#xff1a;Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙… 作者简介大家好我是爱敲代码的小黄独角兽企业的Java开发工程师CSDN博客专家阿里云专家博主系列专栏Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙、Spring从成神到升仙系列如果感觉博主的文章还不错的话请三连支持一下博主哦博主正在努力完成2023计划中以梦为马扬帆起航2023追梦人联系方式hls1793929520加我进群大家一起学习一起进步 文章目录Spring 循环依赖源码解析一、引言二、循环依赖场景1、有参构造引起的循环依赖2、属性注入引起的循环依赖三、循环依赖的原因1、有参构造失败的原因2、属性注入成功的原因2.1 AOP导致的循环依赖四、循环依赖 Spring 源码剖析步骤一查询 MyDemo1 是否存在步骤二将 MyDemo1 半实例化放至缓存中步骤三、四查询 MyDemo2 的缓存是否存在步骤五将 MyDemo2 半实例化放至缓存中步骤六从缓存中获取 MyDemo1步骤七将 MyDemo2 生成的实例化放至 singletonObject 中步骤八将 MyDemo1 生成的实例化放至 singletonObject 中五、总结Spring 循环依赖源码解析
一、引言
对于Java开发者而言关于 Spring 我们一般当做黑盒来进行使用不需要去打开这个黑盒。
但随着目前程序员行业的发展我们有必要打开这个黑盒去探索其中的奥妙。
本期 Spring 源码解析系列文章将带你领略 Spring 源码的奥秘
本期源码文章吸收了之前 Kafka 源码文章的错误将不再一行一行的带大家分析源码我们将一些不重要的部分当做黑盒处理以便我们更快、更有效的阅读源码。
废话不多说发车 本文流程图可关注公众号爱敲代码的小黄回复循环依赖 获取 贴心的小黄为大家准备的文件格式为 POS文件方便大家直接导入 ProcessOn 修改使用 二、循环依赖场景
我们上几篇文章讲解了 IOC、AOP的源码实现如果没有看过的同学可以去看一下
Spring IOC 源码剖析Spring AOP 源码剖析
如果上面的文章你已经熟悉了那么对于循环依赖的理解就会变得很简单甚至你自己都能够想明白整个运行原理
我们首先介绍一下循环依赖的场景
我们在委托 Spring 进行对象的创建时会遇到下面的情况
1、有参构造引起的循环依赖
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文件配置
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean idmyDemo1 classcn.hls.demo1.MyDemo1constructor-arg valuemyDemo2//beanbean idmyDemo2 classcn.hls.demo1.MyDemo2constructor-arg valuemyDemo1//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?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 version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean idmyDemo1 classcn.hls.demo1.MyDemo1property namemyDemo2 refmyDemo2//beanbean idmyDemo2 classcn.hls.demo1.MyDemo2property namemyDemo1 refmyDemo1//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到这里有没有一点点惊讶、一点点懵逼、一点点卧槽
如果有的话那这篇文章将带你解析为什么两种方式不同的注入方式
一种可能正常运行一种不能正常运行
三、循环依赖的原因
这里我们搬出 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 ClassT 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;
}步骤二将 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 ClassT 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);}
}步骤五将 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 ClassT 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 T doGetBean(String name, Nullable ClassT 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);}
}此时各缓存情况 到这里我们的循环依赖的整体流程就被解决了
五、总结
又是一篇大工程的文章结束了
记得校招时候当时对 Spring 懵懂无知转眼间也被迫看了源码
更可怕的是现在面试竟然百分之80都要熟悉IOC、AOP的源码甚至手写 AOP 的实现
但通过这篇文章我相信99% 的人应该都可以理解了 Spring 循环依赖 的实现
那么如何证明你真的理解了 Spring 循环依赖 呢我这里出个经典的题目大家可以想一下为什么Spring要用三级缓存二级不可以嘛
如果你能看到这那博主必须要给你一个大大的鼓励谢谢你的支持
喜欢的可以点个关注Spring 系列到此正式结束了~
【Spring从成神到升仙系列 一】2023年再不会动态代理就要被淘汰了【Spring从成神到升仙系列 二】2023年再不会 IOC 源码就要被淘汰了【Spring从成神到升仙系列 三】2023年再不会 AOP 源码就要被淘汰了【Spring从成神到升仙系列 四】从源码分析 Spring 事务的来龙去脉
后续博主应该会更新 dubbo 或者 并发编程 的系列文章
我是爱敲代码的小黄独角兽企业的Java开发工程师CSDN博客专家Java领域新星创作者喜欢后端架构和中间件源码。
我们下期再见。