10、Spring源码之循环依赖

背景:

1、你知道Spring的一级、二级、三级 缓存分别存放的什么类型的Bean嘛?

  • singletonObjects:         一级缓存,实例化且初始化完成的完整对象
  • earlySingletonObjects: 二级缓存、实例化完成但未初始话的半成品对象
  • singletonFactories:       三级缓存、lambda表达式 用来返回代理或原始对象

2、一般面试经常面试会问Spring是如何解决循环依赖问题的?

  • 很多人会回答使用三级缓存。其实最终是二级缓存(提前暴露),Spring使用三级缓存最终解决的不是循环依赖问题 而是代理问题。如果单纯想解决循环依赖问题、使用二级缓存足矣(下面我会尝试修改源码使用二级缓存单纯解决循环依赖问题)。

3、既然二级缓存能够解决循环依赖问题 Spring为什么又引入了三级缓存呢?

  • 因为在Bean的创建过程、Spring无法预先知道到底生成Bean的对象到底是原始对象或者代理  对象、所以需要设计这样的一个lambda表达式回调机制、来动态判断返回代理对象还是原生对象.当三级缓存lambda表达式执行完 放入二级缓存 三级缓存清空。保证Bean的生命周期。

    假如我设计这样一种依赖   A依赖B、B依赖A、C对象、C依赖A对象。A需要代理对象。
    此时Spring只有一级缓存和二级缓存,根据上面得推理二级缓存肯定是存储一个lambda回调机制, 创建过程如下
    A 实例化后 lambda放入二级缓存  -> 填充A的B属性  ->   B实例化后 lambda放入二级缓存  -> 
    填充B的A属性 ->  调用lambda表达式生成A的代理对象  ->  填充B的C属性 ->  C实例化后 lambda放入二级缓存 -> (一级缓存还没有A)调用lambda表达式生成A的代理对象 -> C对象进入一级缓存  -> B对象放入一级缓存 -> A对象放入一级缓存。

    从上面可以看出经过两次调用lambda表达式生成A对象 无法保证单例。换句话说、B的A属性和C的A属性内存地址是不同的。

知识储备:

Spring的Bean的生命周期要比较熟悉


什么是循环依赖:

 此时,图中是一个闭环,如果想解决这个问题,那么就必须要保证不会出现第二次创建A对象这个步骤,也就是说从容器中获取A的时候必须要能够获取到

思考,在spring中,对象的创建可以分为实例化和初始化,实例化好单未完成初始化的对象是可以直接给其他对象引用的,所以此时可以做一件事,把完成实例化但未完成初始化的对象提前暴露出去,让其他对象能够进行引用,就完成了这个闭环的解环操作,这也就是常规说的提前暴露对象


Debug案例:

  • 配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	   xmlns:context="http://www.springframework.org/schema/context"
    	   xmlns:aop="http://www.springframework.org/schema/aop"
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
    		http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
    ">
    	<context:component-scan base-package="com.zhangfuyin.circle"/>
    
    
    </beans>

  • java
@Component
public class A {
	@Autowired
	private B b;

}


=============================================================================

@Component
public class B {
	@Autowired
	private A a;
}

源码跟踪:

提示:因为Spring循环依赖用贴代码的方式 还是很难理解、因为Spring的创建是一个递归的过程、且几乎涵盖Bean的生命周期的所有方法,这里使用流程图的方式比较容易表达清楚、我会在流程图上标注重要的方法。大家按照数字(实际是递归、我拆了出来)顺序肯定能够明白Spring是如何解决循环依赖的

现象:

A对象:经历了从  三级缓存 ----> 二级缓存  --------> 一级缓存 

B对象:经历了从  三级缓存 -----> 一级缓存

总结:

1、三级缓存中并不是严格的从三级到二级到一级,这样的一个对象转化顺序,有可能在三级和一级中有对象,也有可能再一二三级中都有对象
2、三级缓存:存放的Lambda表达式
3、二级缓存:存放半成品对象
4、一级缓存:存放完整对象

思考:根据流程图,如果说不考虑代理的问题,我如何修改源码 直接用二级缓存就能解决循环依赖问题呢?

无非就改两点:
①放对象的时候不放入三级缓存 即:addSingletonFactory里面的逻辑改成 将实例化好的对象放入二级缓存

②取对象的时候不向三级缓存获取,即:getSingleton 吧获取三级的逻辑去掉、直接去二级缓存获取

<think>嗯,用户想了解Spring框架中循环依赖源码实现。首先,我需要回忆一下Spring处理循环依赖的基本机制。记得Spring使用三级缓存来解决循环依赖的问题,分别是singletonObjects、earlySingletonObjects和singletonFactories。不过具体每个缓存的作用和源码中的处理流程可能需要详细梳理。 接下来,我应该从Spring容器的启动流程开始,比如refresh()方法,这是容器初始化的入口。在refresh()中,有一个步骤是finishBeanFactoryInitialization,这里会实例化所有非延迟加载的单例Bean。当Bean创建时,会调用getBean方法,进而触发doCreateBean。 在doCreateBean过程中,Spring会先通过createBeanInstance创建Bean的实例,然后放入三级缓存singletonFactories中。这时候如果Bean被其他Bean依赖,Spring可以通过ObjectFactory提前暴露这个半成品的Bean。当处理属性注入时,如果发现循环依赖,就会从缓存中获取早期引用。 需要关注的是,为什么需要三级缓存而不是两级?可能和AOP代理有关,因为singletonFactories中的ObjectFactory可以处理代理对象的生成,而earlySingletonObjects只是缓存早期对象,避免重复创建。 然后,用户提供的引用中提到第三级缓存的作用,可能需要结合源码中的getSingleton方法分析。当从缓存中获取Bean时,首先检查一级缓存,如果没有则查看二级缓存,再没有就从三级缓存中的ObjectFactory获取,并将其提升到二级缓存。 另外,源码中的addSingletonFactory方法将Bean的ObjectFactory添加到三级缓存,这一步是在Bean实例化之后、属性填充之前完成的。这样,当发生循环依赖时,依赖方能够通过getSingleton方法获取到早期对象。 可能还需要解释整个流程中的步骤,比如Bean的创建、三级缓存的添加、属性填充时的依赖解析,以及最终将完整Bean放入一级缓存的过程。同时,用户的问题可能涉及到为什么三级缓存是必要的,这涉及到Spring如何处理代理对象,因为代理需要在不同的阶段生成,而三级缓存提供了生成代理的灵活性。 最后,要确保回答结构清晰,分步骤解析源码流程,并引用用户提供的三个引用,特别是关于singletonFactories的作用和refresh方法的起点。同时,生成的相关问题要围绕循环依赖的解决方案、三级缓存的必要性以及AOP的影响展开。</think>### Spring框架循环依赖源码解析 Spring通过**三级缓存**解决循环依赖问题,核心逻辑在`DefaultSingletonBeanRegistry`类中。以下是源码实现的关键步骤: #### 一、三级缓存定义 ```java // 第一级缓存:存放完全初始化好的Bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 第二级缓存:存放早期暴露的Bean(未完成属性填充) private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); // 第三级缓存:存放Bean工厂(用于创建代理对象) private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); ``` #### 二、核心处理流程 1. **Bean实例化阶段** 在`AbstractAutowireCapableBeanFactory.createBeanInstance()`方法中完成对象创建: ```java protected BeanWrapper createBeanInstance(...) { // 通过反射创建Bean实例 return instantiateBean(beanName, mbd); } ``` 2. **提前暴露引用** 在`AbstractAutowireCapableBeanFactory.doCreateBean()`中,通过`addSingletonFactory`将半成品对象加入三级缓存[^2]: ```java protected Object doCreateBean(...) { // 实例化后立即暴露ObjectFactory addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); // 继续执行属性填充 populateBean(beanName, mbd, instanceWrapper); } ``` 3. **依赖解析阶段** 在`DefaultSingletonBeanRegistry.getSingleton()`方法中处理依赖查找: ```java protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 先查二级缓存 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 从三级缓存获取ObjectFactory并创建代理对象 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } return singletonObject; } ``` #### 三、三级缓存必要性 第三级缓存`singletonFactories`主要解决**AOP代理问题**。当存在循环依赖时,Spring需要通过`SmartInstantiationAwareBeanPostProcessor`创建代理对象,而这一过程需要在对象初始化阶段完成。 #### 四、完整处理流程示例 ``` 创建A实例 → 放入三级缓存 → 填充B属性 ↓ 创建B实例 → 放入三级缓存 → 填充A属性 ↓ 从三级缓存获取A的ObjectFactory → 生成代理对象 → 放入二级缓存 ↓ B完成初始化 → 放入一级缓存 ↓ A完成初始化 → 替换二级缓存中的代理对象 → 放入一级缓存 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值