前置知识

  • 只有单例模式下的bean会通过三级缓存提前暴露来解决循环依赖的问题。而非单例的bean每次获取都会重新创建,并不会放入三级缓存,所以多实例的bean循环依赖问题不能解决。

  • 首先需要明白处于各个阶段的bean被放在哪里。在DefaultSingletonBeanRegistry类中

    /** 一级缓存,存放经历完整生命周期的bean*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); /** 三级缓存,存放FactoryBean */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); /** 二级缓存,存放刚刚创建出来还未进行属性赋值的bean */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

测试代码

public class ServiceAA {

    public ServiceAA() {
System.out.println("AA已经创建");
} private ServiceBB serviceBB; public ServiceBB getServiceBB() {
return serviceBB;
} public void setServiceBB(ServiceBB serviceBB) {
this.serviceBB = serviceBB;
}
}
public class ServiceBB {
public ServiceBB() {
System.out.println("BB已经创建");
}
private ServiceAA serviceAA; public ServiceAA getServiceAA() {
return serviceAA;
} public void setServiceAA(ServiceAA serviceAA) {
this.serviceAA = serviceAA;
}
}
    <bean id="serviceAA" class="org.song.circularDepend.ServiceAA" >
<property name="serviceBB" ref="serviceBB"/>
</bean> <bean id="serviceBB" class="org.song.circularDepend.ServiceBB">
<property name="serviceAA" ref="serviceAA"/>
</bean>

流程

  • 在上述代码中,ServiceAA 依赖于ServiceBB,同时ServiceBB也依赖于ServiceAA ,各个bean相互依赖,最终形成了闭环,那么spring是如何解决此问题的呢?

  • 可以简单理解为spring创建bean分为两个步骤,第一步是创建原始bean,第二步就是对属性进行赋值和初始化等操作。

  • 每次创建bean之前都会去缓存中查找是否有当前bean,因为是单例,只能有一个

  • 当创建bean serviceAA后,会将其加入三级缓存中,然后需要填充bean的属性了

  • 这时候发现需要依赖于bean serviceBB,接下来又去创建serviceBB。重复1-3的流程

  • 这时候发现需要依赖于bean serviceAA,发现三级缓存中存在bean serviceAA,所以不需要重复创建。把bean serviceAA注入bean serviceBB

  • 这个时候bean serviceBB创建好了,递归继续去为bean serviceAA进行属性赋值。闭环完成

源码

	public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
try { //初始化剩下的单实例bean
finishBeanFactoryInitialization(beanFactory); } catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//实例化单实例bean
beanFactory.preInstantiateSingletons();
}
@Override
public void preInstantiateSingletons() throws BeansException { getBean(beanName); }
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException { final String beanName = transformedBeanName(name);
Object bean; //首先查询缓存中是否存在bean,当前为null
Object sharedInstance = getSingleton(beanName);
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
//创建bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//一级缓存中是否存在当前bean(serviceAA),此时为null
//isSingletonCurrentlyInCreation(beanName),当前bean是否被标记为
//正在创建,返回false,所以后续不执行,直接返回null
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
beforeSingletonCreation(beanName); singletonObject = singletonFactory.getObject(); addSingleton(beanName, singletonObject); return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
protected void beforeSingletonCreation(String beanName) {
//将当前正在创建的bean serviceAA 标记为正在创建中,添加到集合singletonsCurrentlyInCreation中
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
//开始真正的创建bean service AA
Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance;
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
//用反射创建初始bean serviceAA,源码部分略过
instanceWrapper = createBeanInstance(beanName, mbd, args);
//当前为true
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//如果一级缓存中没有当前bean,就将当前bean放入三级缓存
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// 初始化bean
Object exposedObject = bean;
//为当前bean serviceAA赋值
populateBean(beanName, mbd, instanceWrapper);
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
} return exposedObject;
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
//将当前bean serviceAA放入三级缓存,key为beanname value为创建该bean的工厂
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
//获取当前bean sercieAA所有的属性
PropertyValues pvs = mbd.getPropertyValues();
//......对属性的解析等操作略过 //开始注入属性
applyPropertyValues(beanName, mbd, bw, pvs);
}
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {

   //封装所有属性的集合,略过
List<PropertyValue> original; for (PropertyValue pv : original) {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
//解析引用类型的属性
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
}
//应用属性过程略过
}
public Object resolveValueIfNecessary(Object argName, Object value) {
//判断是否是运行时bean引用
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
//解析此引用 service BB
return resolveReference(argName, ref);
}
private Object resolveReference(Object argName, RuntimeBeanReference ref) {

    String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
//开始递归调用getbean方法,当前bean为serviceBB
Object bean = this.beanFactory.getBean(refName); }
  • 此时serviceBB开始递归调用getBean()方法,开始重复上述流程,关注流程第五步。此时bean serviceBB实例化完成,并且开始为属性赋值,发现有一个属性为serviceAA。于是尝试获取serviceAA。

  • 进入this.beanFactory.getBean()方法。进入doGetBean()方法。后进入getSingleton(String beanName, boolean allowEarlyReference)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//一级缓存中是否存在当前bean(serviceAA),此时为null
//isSingletonCurrentlyInCreation(beanName),当前bean是否被标记为
//正在创建,当前为true,进入判断
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//判断二级缓存中是否有bean serviceAA,当前返回null
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//通过beanname获得了之前在三级缓存中添加的创建serviceAA的工厂方法
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//此时singletonObject指向之前实例化完成的serviceAA对象
singletonObject = singletonFactory.getObject();
//将serviceAA添加到二级缓存中,并从三级缓存中删除
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
  • 此时serviceBB的属性赋值完毕,serviceBB的populateBean方法执行结束。其属性service AA指向还未执行完成populateBean()方法的bean serviceAA,等待后续的递归返回。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//将serviceBB放入一级缓存
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
//从三级缓存,二级缓存中删除
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
  • 此时serviceBB创建完毕。递归返回继续还未属性赋值完成的bean serviceAA。调用 addSingleton()方法,同上将bean serviceAA放入一级缓存

总结

欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!

建议收藏!利用Spring解决循环依赖,深入源码给你讲明白!的更多相关文章

  1. Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗

    开心一刻 吃完晚饭,坐在院子里和父亲聊天 父亲:你有什么人生追求? 我:金钱和美女 父亲对着我的头就是一丁弓,说道:小小年纪,怎么这么庸俗,重说一次 我:事业与爱情 父亲赞赏的摸了我的头,说道:嗯嗯, ...

  2. Spring解决循环依赖

    1.Spring解决循环依赖 什么是循环依赖:比如A引用B,B引用C,C引用A,它们最终形成一个依赖环. 循环依赖有两种 1.构造器循环依赖 构造器注入导致的循环依赖,Spring是无法解决的,只能抛 ...

  3. springboot bean的循环依赖实现 源码分析

    springboot bean的循环依赖实现 源码分析 本文基于springboot版本2.5.1 <parent> <groupId>org.springframework. ...

  4. 曹工说Spring Boot源码(29)-- Spring 解决循环依赖为什么使用三级缓存,而不是二级缓存

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  5. 浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种: ...

  6. Spring解决循环依赖,你真的懂了吗?

    导读 前几天发表的文章SpringBoot多数据源动态切换和SpringBoot整合多数据源的巨坑中,提到了一个坑就是动态数据源添加@Primary接口就会造成循环依赖异常,如下图: 这个就是典型的构 ...

  7. 一张图彻底理解Spring如何解决循环依赖!!

    写在前面 最近,在看Spring源码,看到Spring解决循环依赖问题的源码时,不得不说,源码写的太烂了.像Spring这种顶级的项目源码,竟然存在着这种xxx的代码.看了几次都有点头大,相信很多小伙 ...

  8. Spring如何解决循环依赖问题

    目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...

  9. 彻底理解Spring如何解决循环依赖

    Spring bean生命周期 可以简化为以下5步. 1.构建BeanDefinition 2.实例化 Instantiation 3.属性赋值 Populate 4.初始化 Initializati ...

随机推荐

  1. Luogu P2656 采蘑菇

    尽管是缩点的习题,思路也是在看了题解后才明白的. 首先,每个强连通分量内的点都是一定互通的,也就是可以完全把这里面的边都跑满,摘掉所有能摘的蘑菇.那么,考虑给每一个强连通分量化为的新点一个点权,代表摘 ...

  2. Hibernate的配置跟简单创建一个表并插入一条数据

    首先官网下载一个hibernate的架包,由于时间关系,博主已经分享到了百度网盘:https://pan.baidu.com/s/1Mw0Og3EKnouf84Njz9UicQ,提取码lens Hib ...

  3. 容器中实现拉取其它服务器的jar包程序

    缘由:在做接口自动化测试时,若业务场景有一个前置仓库,在该仓库内完成一系列的场景测试,一旦某一场景测试失败,脏数据对环境造成影响则需要清理: 1.我容器的内核系统为Debian GNU/Linux 1 ...

  4. ios中关于系统定位CLLocationManager的使用解析

    //1.添加定位管理委托协议 CLLocationManagerDelegate //2.初始化定位管理对象 self.locationManager=[[CLLocationManager allo ...

  5. Appium之测试微信小程序

    坚持原创输出,点击蓝字关注我吧 作者:清菡 博客:Oschina.云+社区.知乎等各大平台都有. 目录 一.往期回顾 二.测试微信小程序 1.准备工作 2.操作步骤 3.注意 4.强制设置安卓的进程 ...

  6. moviepy音视频剪辑:使用concatenate_videoclips和clips_array将多个视频合成一个顺序播放或同屏播放的视频

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 一.视频合成概述 视频合成,也称为非线性编辑,实际 ...

  7. 第11.18节 Python 中re模块的匹配对象

    匹配对象是Python中re模块正则表达式匹配处理的返回结果,用于存放匹配的情况.老猿认为匹配对象更多的应该是与组匹配模式的功能对应的,只是没有使用组匹配模式的正则表达式整体作为组0. 为了说明下面的 ...

  8. PyQt(Python+Qt)学习随笔:QTreeView树形视图的sortingEnabled属性

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTreeView树形视图的sortingEnabled属性用于控制视图中的数据是否启用按表头排序, ...

  9. kaggle——Bag of Words Meets Bags of Popcorn(IMDB电影评论情感分类实践)

    kaggle链接:https://www.kaggle.com/c/word2vec-nlp-tutorial/overview 简介:给出 50,000 IMDB movie reviews,进行0 ...

  10. LeetCode初级算法之数组:36 有效数独

    有效数独 题目地址:https://leetcode-cn.com/problems/valid-sudoku/ 判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可. ...