spring单列bean之循环依赖核心源码解读

在springBean单例模式下:

Spring 单例 Bean 的创建核心逻辑位于 AbstractAutowireCapableBeanFactory 类的 doCreateBean 方法中。整个过程可以概括为:‌实例化 -> 属性填充 -> 初始化 -> 注册销毁回调‌。

以下是基于 Spring Framework 5.x/6.x 的核心源码流程解析:

  1. 入口:getBean -> doGetBean -> createBean

    当调用 context.getBean("beanName") 时,最终会进入 AbstractAutowireCapableBeanFactory.doCreateBean。

  2. 核心步骤详解 (doCreateBean)

    第一步:实例化 (Instantiation)

    创建 Bean 的原始对象(Raw Object),此时属性尚未填充。

java 复制代码
// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) {
    
    // 1. 实例化 Bean
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        // createBeanInstance 负责推断构造方法并反射创建对象
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // 获取原始对象实例
    Object bean = instanceWrapper.getWrappedInstance();
    
    // ... 省略早期引用暴露逻辑(解决循环依赖的关键) ...
}

‌关键点‌:这里通过 instantiateBean 使用反射调用构造函数。如果存在工厂方法或 Supplier,也会在此处理。

第二步:属性填充 (Populate Bean)

将依赖注入到 Bean 中(即处理 @Autowired, @Value, 等)。

java 复制代码
    // 2. 属性填充
    populateBean(beanName, mbd, instanceWrapper);

‌关键点‌:

populateBean 会先执行 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation。

然后解析依赖关系,递归调用 getBean 获取依赖项。

最后通过反射或 Setter 方法将依赖注入到当前 Bean 中。

第三步:初始化 (Initialization)

执行 Bean 的生命周期回调方法。

java 复制代码
    // 3. 初始化 Bean
    exposedObject = initializeBean(beanName, exposedObject, mbd);

initializeBean 内部执行顺序非常严格:

‌Aware 接口回调‌:

如果 Bean 实现了 BeanNameAware, BeanFactoryAware, ApplicationContextAware 等,此时调用 setBeanName, setBeanFactory 等方法。

‌前置处理器 (BeanPostProcessor.postProcessBeforeInitialization)‌:

执行所有注册的 BPP 的 postProcessBeforeInitialization 方法。

‌重要‌:@PostConstruct 注解的方法通常由 CommonAnnotationBeanPostProcessor 在此阶段执行。

‌自定义初始化方法‌:

如果 Bean 实现了 InitializingBean 接口,调用 afterPropertiesSet()。

如果配置了 init-method,通过反射调用该方法。

‌后置处理器 (BeanPostProcessor.postProcessAfterInitialization)‌:

执行所有注册的 BPP 的 postProcessAfterInitialization 方法。

‌重要‌:AOP 代理对象通常在此阶段创建(如 AnnotationAwareAspectJAutoProxyCreator)。如果生成了代理对象,此处返回的是‌代理对象‌而非原始对象。

第四步:注册销毁回调 (Register Disposable Bean)

如果 Bean 是单例且定义了销毁方法,将其注册到容器中,以便容器关闭时执行。

java 复制代码
    // 4. 注册销毁回调
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
  1. 循环依赖的处理(三级缓存)
    在单例 Bean 创建过程中,Spring 通过‌三级缓存‌解决单例字段的循环依赖问题:

‌一级缓存 (singletonObjects)‌:存放完全初始化好的 Bean。

‌二级缓存 (earlySingletonObjects)‌:存放原始的 Bean 对象(尚未填充属性),用于解决循环依赖时的提前暴露。

‌三级缓存 (singletonFactories)‌:存放 ObjectFactory,主要用于处理 AOP 代理。如果 Bean 需要被代理,则通过工厂提前生成代理对象放入二级缓存。

‌流程简述‌:

A 依赖 B,B 依赖 A。

A 实例化后,将 A 的工厂放入三级缓存。

A 填充属性时发现需要 B,去创建 B。

B 填充属性时发现需要 A,调用 getBean("A")。

此时 A 未创建完成,但在三级缓存中有工厂。Spring 调用工厂获取 A 的早期引用(如果有 AOP 则生成代理),放入二级缓存。

B 拿到 A 的早期引用,完成 B 的创建。

A 拿到完整的 B,完成 A 的创建。

下面就看下spring解决三级缓存的核心源码:

核心就3个map,用来存放bean

java 复制代码
## 一级缓存存放完整的bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

## 三级缓存是存放bean工厂的,作用是用来生产早期的bean
	/** Creation-time registry of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
## 二级缓存是用来存放早期未初始化完成的bean
		/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
java 复制代码
protected @Nullable Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock.
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				if (!this.singletonLock.tryLock()) {
					// Avoid early singleton inference outside of original creation thread.
					return null;
				}
				try {
					// Consistent creation of early reference within full singleton lock.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								// Singleton could have been added or removed in the meantime.
								if (this.singletonFactories.remove(beanName) != null) {
									this.earlySingletonObjects.put(beanName, singletonObject);
								}
								else {
									singletonObject = this.singletonObjects.get(beanName);
								}
							}
						}
					}
				}
				finally {
					this.singletonLock.unlock();
				}
			}
		}
		return singletonObject;
	}

上面这段核心代码不用解读了吧,如果看不懂让AI来帮你解释一下,今天就分享到这里,下次我们在讨论为啥要设计3级缓存,2级不行吗?

相关推荐
苏三说技术1 小时前
推荐一个牛逼的企业知识库系统
后端
我命由我123451 小时前
RFID 技术极简理解
java·c语言·c++·嵌入式硬件·物联网·visualstudio·java-ee
大刚测试开发实战1 小时前
TestHub数据工厂发布!附更新指南
前端·后端·github
格发许可优化管理系统1 小时前
Mentor许可证与其他软件许可证的深度比较
java·大数据·运维·c语言·c++·算法
pingglala1 小时前
winscp连接linux失败解决方法
java·linux·服务器
Javatutouhouduan1 小时前
深入学习JVM底层原理:源码剖析与实例详解!
java·jvm·java面试·后端开发·java程序员·java八股文·java性能优化
Flynt1 小时前
我把 JDK21 虚拟线程用成了"性能灾难",复盘完发现踩了三个大坑
java·ai编程
AINative软件工程1 小时前
LLM 应用的 Schema 演进工程:structured output 字段改了,下游为什么炸了?
后端·python·架构
做一个快乐的小傻瓜2 小时前
ZYNQ DEV套件引脚约束
java·linux·运维