前置知识

  • 只有单例模式下的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. Java基础教程——Java简介

    Java是SUN公司1995年推出的一门高级编程语言,是一种面向Internet的编程语言,随着Java技术在Web方面的不断成熟,已经成为Web应用程序的首选开发语言. (2009年SUN公司被Or ...

  2. 基于Docker搭建pypi私有仓库

    一.搭建 1.准备htpasswd.txt文件 该文件内容包含上传包至仓库时验证的用户名和密码 pip install htpasswd htpasswd -sc htpasswd.txt <u ...

  3. Kafka入门之consumer--rebalance流程

    重平衡(rebalance) 旧版本Kafka依托于Zk进行rebalance,新版本consumer使用了Kafka内置的一个全新的组协调协议.对于每个组而言,Kafka的某个broker会被选举为 ...

  4. ResHacker 用命令行方式修改 windows PE文件版本号

    由于工作需要在詹金斯(genkins)集成环境打包,打包避免不了需要修改版本号,写入版本号最简单的方式通过修改windows rc文件 这就意味着,每次构建新版本前需要修改一次源文件 这个在用詹金斯集 ...

  5. LeetCode 046 Permutations

    题目要求:Permutations(全排列) Given a collection of numbers, return all possible permutations. For example, ...

  6. 新手上路之如何选择Java版本

    @ 目录 LTS与非LTS LTS 非LTS Java CPU与PSU Java SE.Java EE.Java ME的区别 Java SE Java EE Java ME 每一次JDK上新总有一群人 ...

  7. argparse使用范例

    if __name__ == "__main__": # https://docs.python.org/zh-cn/dev/library/argparse.html impor ...

  8. NOIP2020退役记

    Day 0 这一次因为疫情仍然没有试机,但是允许提前打开虚拟机,减少了调试虚拟机的时间 开考时,吸取上一次 \(CSP\) 的教训,先把题面看了一遍 感觉 \(T1\) 比较可做,剩下的暂时没有思路 ...

  9. PyQt(Python+Qt)学习随笔:枚举类QTreeWidgetItem.ItemType、QListWidgetItem.ItemType的取值及含义

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 在Model/View的便利类QTreeWidget.QListWidgetItem中的项类型分别是 ...

  10. PyQt(Python+Qt)学习随笔:QColumnView的resizeGripsVisible属性

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QColumnView在一个视图中展示多个列表,每个下级列表是上一个列表的数据项的分支, QColu ...