前置知识

  • 只有单例模式下的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. JDBC【2】-- JDBC工作原理以及简单封装

    目录 1. 工作原理 1.1 加载驱动 1.1.1 类加载相关知识 1.1.2 为什么JDK 1.6之后不需要显示加载了? 1.2 驱动加载完成了,然后呢? 2. 简单封装 1. 工作原理 一般我们主 ...

  2. Java集合【2】--iterator接口详解

    目录 一.iterator接口介绍 二.为什么需要iterator接口 三.iterator接口相关接口 3.1 ListIterator 3.2 SpitIterator 3.2.1 SpitIte ...

  3. C++基础知识篇:C++ 变量类型

    变量其实只不过是程序可操作的存储区的名称.C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上. 变量的名称可以由字母.数字和下划线字 ...

  4. 最佳置换算法OPT

    原文链接:https://www.jianshu.com/p/544ee20e307c

  5. 【VSA】One-shot video-based person re-identification with variance subsampling algorithm

    目录 解决了什么问题 主要贡献和创新点 基本框架 提出的方法 01 variance confidence方差置信度 02 Variance Subsampling Algorithm 方差二次采样算 ...

  6. charles功能(四) 模拟 接口404/403返回值(blacklist方法)

    1.tools-->blacklist 2.允许启用黑名单选择接口返回错误的形式 (404或者403),添加接口地址并保存 3.再次请求效果如下

  7. C++编程指南续(4-5)

    五.常量 常量是一种标识符,它的值在运行期间恒定不变.C语言用 #define来定义常量(称为宏常量).C++ 语言除了 #define外还可以用const来定义常量(称为const常量). 5.1  ...

  8. SpringCloud 源码系列(3)—— 注册中心 Eureka(下)

    十一.Eureka Server 集群 在实际的生产环境中,可能有几十个或者几百个的微服务实例,Eureka Server 承担了非常高的负载,而且为了保证注册中心高可用,一般都要部署成集群的,下面就 ...

  9. PyQt(Python+Qt)学习随笔:部件的inputMethodHints属性

    inputMethodHints属性只对输入部件有效,输入法使用它来检索有关输入法应如何操作的提示,例如,如果设置了只允许输入数字的标志,则输入法可能会更改其可视组件,以反映只能输入数字.相关取值及含 ...

  10. python安装Scrapy框架

    看到自己写的惨不忍睹的爬虫,觉得还是学一下Scrapy框架,停止一直造轮子的行为 我这里是windows10平台,python2和python3共存,这里就写python2.7安装配置Scrapy框架 ...