Spring源码之循环依赖
https://www.cnblogs.com/longy2012/articles/12834762.html
https://www.bilibili.com/video/BV1iD4y1o7pM?p=7
https://www.jianshu.com/p/8bb67ca11831
https://cloud.tencent.com/developer/article/1497692
https://blog.nowcoder.net/n/2bb528b258b44c7eab1703a52170ef09
总结
- 在DefaultSingletonBeanRegistry类getSingleton方法中将beanName(a)加入到singletonsCurrentlyInCreation set集合中
- A进行实例化(未初始化)
- A加入三级缓存singletonFactories中
- 在AbstractAutowireCapableBeanFactory中populateBean方法中开始属性填充(field B)(调用AutowiredAnnotationBeanPostProcessor类中postProcessProperties方法)
- 在DefaultSingletonBeanRegistry类getSingleton方法中将beanName(b)加入到singletonsCurrentlyInCreation set集合中
- 将B加入singletonsCurrentlyInCreation,标志为正在创建中
- B进行实例化(未初始化)
- B加入三级缓存singletonFactories中
- 开始对B类进行属性填充(A field)
- 重新走到AbstractBeanFactory中doGetBean方法,调用DefaultSingletonBeanRegistry类getSingleton方法,将A put到二级缓存earlySingletonObjects中,并在三级缓存singletonFactories中移除
- B进行初始化
- getSingleton方法中调用DefaultSingletonBeanRegistry类afterSingletonCreation方法,将beanName(b)从singletonsCurrentlyInCreation移除
- 走到DefaultSingletonBeanRegistry中getSingleton方法finally中,将B加入一级缓存,并从三级缓存中移除
- 将B返回,A进行初始化
- getSingleton方法中调用DefaultSingletonBeanRegistry类afterSingletonCreation方法,将beanName(a)从singletonsCurrentlyInCreation移除
- 最后走到DefaultSingletonBeanRegistry中getSingleton方法finally中,将A加入一级缓存,并从二级缓存中移除
示例
- @DependsOn
@DependsOn("b")
@Component
public class A {
}
@DependsOn("a")
@Component
public class B {
}
报错:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined
- field属性注入循环依赖(不报错)
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
源码解析
三级缓存
DefaultSingletonBeanRegistry类中:
/** Cache of singleton objects: bean name to bean instance. */
//一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
//三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
//二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
springboot中入口
调用链:
SpringApplication#run() --> SpringApplication#refreshContext() --> SpringApplication#refresh() --> ServletWebServerApplicationContext#refresh() --> AbstractApplicationContext#refresh() --> AbstractApplicationContext#finishBeanFactoryInitialization() --> DefaultListableBeanFactory#preInstantiateSingletons() --> AbstractBeanFactory#getBean() --> AbstractBeanFactory#doGetBean()
将beanName(a)加入到singletonsCurrentlyInCreation
AbstractBeanFactory#doGetBean()方法中:
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
markBeanAsCreated方法中:
this.alreadyCreated.add(beanName);
在getSingleton方法中,执行
beforeSingletonCreation(beanName);
this.singletonsCurrentlyInCreation.add(beanName)
其中singletonsCurrentlyInCreation为
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
A进行实例化(未初始化)
在在getSingleton方法执行到
singletonObject = singletonFactory.getObject();
调用匿名方法
() -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
调用AbstractAutowireCapableBeanFactory#createBean() --> AbstractAutowireCapableBeanFactory#doCreateBean(),执行如下代码进行实例化:
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
bean(A)具备地址
A加入三级缓存singletonFactories中
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
在populateBean方法中开始对A属性填充
populateBean(beanName, mbd, instanceWrapper);
对B重复上面步骤
调用链
AbstractApplicationContext#refresh() --> AbstractApplicationContext#finishBeanFactoryInitialization() --> DefaultListableBeanFactory#preInstantiateSingletons() --> AbstractBeanFactory#getBean() --> AbstractBeanFactory#doGetBean() --> AbstractAutowireCapableBeanFactory#createBean()
--> AbstractAutowireCapableBeanFactory#doCreateBean()
(上面为A,下面开始A属性B注入)
--> AbstractAutowireCapableBeanFactory#populateBean() --> AutowiredAnnotationBeanPostProcessor#postProcessProperties() --> InjectionMetadata#inject() --> AutowiredAnnotationBeanPostProcessor#inject() --> DefaultListableBeanFactory#resolveDependency() --> DefaultListableBeanFactory#doResolveDependency() --> DependencyDescriptor#resolveCandidate() --> AbstractBeanFactory#getBean() --> AbstractBeanFactory#doGetBean()
会和上面A同样做下面这些操作:
- 在DefaultSingletonBeanRegistry类getSingleton方法中将beanName(b)加入到singletonsCurrentlyInCreation set集合中
- 将B加入singletonsCurrentlyInCreation,标志为正在创建中
- B进行实例化(未初始化,具有B地址)
- B加入三级缓存singletonFactories中
- 开始对B类进行属性填充(A field)
将A put到二级缓存earlySingletonObjects中
重新对A执行doGetBean方法,执行getSingleton时,singletonFactory != null,故A放到二级缓存中,并从三级缓存中移除
Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
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;
}
B进行初始化
此时B完成属性注入,A具有地址,开始初始化:
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
将beanName(b)从singletonsCurrentlyInCreation移除
B执行完AbstractBeanFactory类匿名方法中createBean(beanName, mbd, args),接着getSingleton往下执行,在finally中:
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
将B加入一级缓存,并从三级缓存中移除
在getSingleton方法执行addSingleton(beanName, singletonObject);从而将B从三级缓存中移除,并添加到一级缓存中
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);
}
}
完成A中B属性注入,对A进行初始化
A开始执行下面代码:
exposedObject = initializeBean(beanName, exposedObject, mbd);
开始将beanName(a)从singletonsCurrentlyInCreation移除;并从二级缓存中移除,添加到一级缓存
A接着getSingleton往下执行,在finally中执行afterSingletonCreation将beanName(a)从singletonsCurrentlyInCreation移除;
最后在addSingleton(beanName, singletonObject)将A从二级缓存中移除,添加到一级缓存
面试问题
- 一级缓存是否够用?
不能。多线程情况下,会获取到实例化但没有初始化的对象,属性都为null
- 二级缓存是否够用?
如果创建是普通类,二级缓存满足
- 为什么需要三级缓存?(代理)
在动态代理中,返回是代理类。如果没有三级缓存,最开始放置是实例化好对象,然后缓存有了,后面进行代理处理,那原来的对象是否覆盖??
/**
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference
*/
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;
}
Spring源码之循环依赖的更多相关文章
- Spring源码解析——循环依赖的解决方案
一.前言 承接<Spring源码解析--创建bean>.<Spring源码解析--创建bean的实例>,我们今天接着聊聊,循环依赖的解决方案,即创建bean的ObjectFac ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- 3.2spring源码系列----循环依赖源码分析
首先,我们在3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖 中手写了循环依赖的实现. 这个实现就是模拟的spring的循环依赖. 目的是为了更容易理解spring源码 ...
- 3.4 spring5源码系列--循环依赖的设计思想
前面已经写了关于三篇循环依赖的文章, 这是一个总结篇 第一篇: 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖 第二篇: 3.2spring源码系列----循环依赖源 ...
- 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖
本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...
- 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析
目录 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGetBean AbstractAutowireC ...
- 【Spring源码解析】—— 依赖注入结合SpringMVC Demo-xml配置理解
在IOC容器初始化的梳理之后,对依赖注入做一个总结,就是bean实例化的过程,bean的定义有两种方式,一种是xml文件配置,一种是注解,这里是对xml配置文件的依赖注入的介绍,后续对bean与该部分 ...
- Spring源码-循环依赖源码解读
Spring源码-循环依赖源码解读 笔者最近无论是看书还是从网上找资料,都没发现对Spring源码是怎么解决循环依赖这一问题的详解,大家都是解释了Spring解决循环依赖的想法(有的解释也不准确,在& ...
- Spring源码分析(十七)循环依赖
本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 实例化bean是一个非常复杂的过程,而其中比较难以理解的就是对循环依赖的解决, ...
随机推荐
- kubernetes:用kubeadm管理token(kubernetes 1.18.3)
一,token的用途: 1,token是node节点用来连接master节点的令牌字串, 它和ca证书的hash值是把一台node节点加入到kubernetes集群时要使用的凭证 2, 通过kubea ...
- centos8上安装mysql8
一,下载并解压mysql8 1,mysql官网 https://www.mysql.com/ 2,下载到source目录 [root@yjweb source]# wget https://cdn.m ...
- linux 压缩 tar命令
linux中tar命令用法 总结 *.tar 用 tar –xvf 解压 *.gz 用 gzip -d或者gunzip 解压 *.tar.gz和*.tgz 用 tar –xzf 解压 *.bz2 ...
- pychartdir模块安装
python模块pychartdir导入问题 在迁移别人写好的脚本时,发现pychartdir没有导入,脚本执行报错.以下是报错内容: [modps@LGJF-ZYC5-MMSC-WEB02 ~]$ ...
- 阿里云ECS磁盘扩容不生效处理办法
原因 阿里云ECS云盘扩容后,在Linux服务器上没有生效 实际需要几条命令扩容才能正式生效. 处理方法 先看扩容前磁盘空间,/dev/vda1可以看到只有40G. # df -Th Filesyst ...
- OpenCV计算机视觉学习(8)——图像轮廓处理(轮廓绘制,轮廓检索,轮廓填充,轮廓近似)
如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 1, ...
- 数位dp(贴一个模板=。=)
emmmm,之前看到大佬的博客感觉这个模板挺有用的,就贴了一个= = 然后解释什么的都有了就...... 数位dp一般应用于: 求出在给定区间[A,B]内,符合条件P(i)的数i的个数. 条件P(i) ...
- vue 404
问题描述:前端同事使用Vue.js框架,利用vue-route结合webpack编写了一个单页路由项目,运维协助在服务器端配置nginx.部署完成后,访问首页没问题,从首页里打开二级页面没问题,但是所 ...
- D. Kilani and the Game 解析(裸BFS、實作)
Codeforce 1105 D. Kilani and the Game 解析(裸BFS.實作) 今天我們來看看CF1105D 題目連結 題目 給一個\(n\times m\)的地圖,地圖上有幾種格 ...
- E. Median String 解析(思維、大數運算)
Codeforce 1144 E. Median String 解析(思維.大數運算) 今天我們來看看CF1144E 題目連結 題目 給你兩個長度為\(k\)的字串\(s\)和\(t\),求字典序排序 ...