前置知识

  • 只有单例模式下的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. Codeforces Round #667 (Div. 3) B、C、D、E 题解

    抱歉B.C题咕了这么久 B. Minimum Product #枚举 #贪心 题目链接 题意 给定四个整数\(a, b, x, y\),其中\(a\geq x, b\geq y\),你可以执行不超过\ ...

  2. IDEA连接码云

    IDEA连接码云: 1.安装Gitee插件 2.下载git.exe https://git-scm.com/download/win 3.安装git 如果是Win10专业版,可能会出错,GitBash ...

  3. Mybatis【2】-- 多个mapper文件以及namespace作用

    多个mapper文件以及namespace作用 要是多个mapper文件的时候怎么处理,namespace又是干什么用的呢 首先我们来看创建数据库语句: #创建数据库 CREATE DATABASE ...

  4. 网络拓扑实例之交换机基于全局地址池作为DHCP服务器(七)

    组网图形 DHCP服务器简介 通常用户希望网络中的每台终端能够动态获取IP地址.DNS服务器的IP地址.路由信息.网关信息等网络参数,不需要手动配置终端的IP地址等网络参数:另外,针对一些移动终端(手 ...

  5. CentOS基础命令使用技巧-1

    less用法 g快速返回文件首部 G快速到文本最末端 /当前行向下搜索 ?当前行向上搜索 n显示下一个 q退出 cp用法 r拷贝一个目录必须加这个 rm用法 r删除目录 f强制删除,r后面删除不存在的 ...

  6. (转)React学习笔记(干货满满)

    1. React 长什么样 React 是 facebook 开源出来的一套前端方案,官网在 https://reactjs.org . 先看一个简单的样子: <!DOCTYPE html> ...

  7. 第8.22节 Python案例详解:重写 “富比较”方法控制比较逻辑

    一. 案例说明 本节定义一个小汽车的类Car,类中包括车名carname.百公里油耗oilcostper100km.价格price三个属性.然后实现__lt__.__gt__.__le__.__ge_ ...

  8. PyQt开发实战: 利用QToolBox开发的桌面工具箱

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 toolBox工具箱是一个容器部件,对应类为QToolBox,在其内有一列从上到下顺序排列 ...

  9. 从零开始的xxe学习

    本文介绍了一个菜鸡对xxe的一步步学习(内容多来源于大佬的博客,先感谢一波) 涉及知识点: (1)xxe 目录: 解析: 1.xxe是什么(不详解了,网上很多的) XXE(XML External E ...

  10. bugkuctf 这 是 一 个 神 奇 的 登 录 界 面

    首先结合源码可以看出这是一道sql注入题. 然后开始萌新的日常操作,尝试单引号闭合,可是并没有用,而且因为单引号注入的题太多,导致并没有立刻开始尝试双引号,之后想起双引号(对,双引号木得牌面)得到如下 ...