
你是否遇到过这样的场景:启动 Spring 应用时突然抛出BeanCurrentlyInCreationException,提示 "Requested bean is currently in creation: Is there an unresolvable circular reference?"?这就是 Spring 中典型的循环依赖问题。当 A 依赖 B,B 又依赖 A 时,就形成了最简单的循环依赖。
如果没有特殊处理,这种相互依赖会导致无穷无尽的初始化过程,最终以失败告终。Spring 通过三级缓存机制优雅地解决了这个问题,但很多开发者只知其然,不知其所以然。本文将从底层原理到实战案例,全方位剖析 Spring 三级缓存的工作机制,让你不仅能解决实际问题,更能理解其设计思想。
在深入三级缓存之前,我们必须先掌握 Spring Bean 的完整生命周期。因为缓存的设计与 Bean 的创建过程紧密相关。
Spring Bean 的创建过程大致可以分为以下阶段:

关键节点说明:
循环依赖可能发生在属性填充阶段。例如:
如果没有缓存机制,Spring 会尝试再次创建 Bean A,导致循环调用。
Spring 通过三级缓存解决循环依赖,这三级缓存分别存储不同状态的 Bean 对象。
在DefaultSingletonBeanRegistry类中,定义了三级缓存的核心数据结构:
/**
* 一级缓存:存储完全初始化完成的单例Bean
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 二级缓存:存储早期暴露的单例Bean(已实例化但未完成初始化)
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
* 三级缓存:存储Bean的工厂对象,用于生成早期暴露的Bean
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

让我们通过 A 依赖 B,B 依赖 A 的经典循环依赖场景,详细分析三级缓存的工作流程。

singletonFactories.put("a", () -> getEarlyBeanReference("a", mbd, a))singletonFactories.put("b", () -> getEarlyBeanReference("b", mbd, b))getObject()方法获取 A 的早期引用你可能会问:为什么不直接使用二级缓存?这涉及到 AOP 代理的场景。
当 Bean 需要被 AOP 增强时,Spring 会创建代理对象。如果在循环依赖中直接暴露原始对象,后续的代理对象创建会导致不一致。三级缓存通过工厂对象延迟创建代理对象,确保在需要时才生成正确的代理对象。
/**
* 从三级缓存获取早期Bean引用的核心方法
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
// 允许BeanPostProcessor(如AOP)修改早期暴露的Bean
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
这个方法会应用所有SmartInstantiationAwareBeanPostProcessor,其中AbstractAutoProxyCreator会在这里创建代理对象(如果需要)。
让我们通过一个实际案例来验证三级缓存的工作机制。
pom.xml 关键依赖:
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.2</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.8</version>
</dependency>
</dependencies>
ServiceA.java:
package com.ken.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 演示循环依赖的ServiceA
*
* @author ken
*/
@Slf4j
@Service
public class ServiceA {
private final ServiceB serviceB;
/**
* 构造方法注入ServiceB
*
* @param serviceB 依赖的ServiceB实例
*/
@Autowired
public ServiceA(ServiceB serviceB) {
log.info("ServiceA构造方法执行");
this.serviceB = serviceB;
}
/**
* 初始化方法
*/
public void init() {
log.info("ServiceA初始化完成");
}
}
ServiceB.java:
package com.ken.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 演示循环依赖的ServiceB
*
* @author ken
*/
@Slf4j
@Service
public class ServiceB {
private final ServiceA serviceA;
/**
* 构造方法注入ServiceA
*
* @param serviceA 依赖的ServiceA实例
*/
@Autowired
public ServiceB(ServiceA serviceA) {
log.info("ServiceB构造方法执行");
this.serviceA = serviceA;
}
/**
* 初始化方法
*/
public void init() {
log.info("ServiceB初始化完成");
}
}
AppConfig.java:
package com.ken.config;
import com.ken.service.ServiceA;
import com.ken.service.ServiceB;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 应用配置类
*
* @author ken
*/
@Configuration
public class AppConfig {
/**
* 定义ServiceA的Bean,指定初始化方法
*
* @return ServiceA实例
*/
@Bean(initMethod = "init")
public ServiceA serviceA() {
return new ServiceA(serviceB());
}
/**
* 定义ServiceB的Bean,指定初始化方法
*
* @return ServiceB实例
*/
@Bean(initMethod = "init")
public ServiceB serviceB() {
return new ServiceB(serviceA());
}
}
Main.java:
package com.ken;
import com.ken.config.AppConfig;
import com.ken.service.ServiceA;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* 主程序入口
*
* @author ken
*/
public class Main {
public static void main(String[] args) {
// 启动Spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取ServiceA实例
ServiceA serviceA = context.getBean(ServiceA.class);
System.out.println("成功获取ServiceA实例:" + serviceA);
}
}
执行 Main 类,控制台输出如下:
ServiceA构造方法执行
ServiceB构造方法执行
ServiceB初始化完成
ServiceA初始化完成
成功获取ServiceA实例:com.ken.service.ServiceA@5a07e868
从输出可以看出:
这证明 Spring 成功解决了循环依赖问题。
让我们修改案例,为 ServiceA 添加 AOP 增强,看看三级缓存如何处理代理对象。
LoggingAspect.java:
package com.ken.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 日志切面,为ServiceA添加增强
*
* @author ken
*/
@Slf4j
@Aspect
@Component
public class LoggingAspect {
/**
* 在ServiceA的所有方法执行前打印日志
*
* @param joinPoint 连接点
*/
@Before("execution(* com.ken.service.ServiceA.*(..))")
public void logBefore(JoinPoint joinPoint) {
log.info("执行ServiceA的方法:{}", joinPoint.getSignature().getName());
}
}
修改 AppConfig.java,添加@EnableAspectJAutoProxy注解启用 AOP:
package com.ken.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
// 其他import省略
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
// 内容不变
}
运行结果:
ServiceA构造方法执行
ServiceB构造方法执行
ServiceB初始化完成
ServiceA初始化完成
成功获取ServiceA实例:com.ken.service.ServiceA$$SpringCGLIB$$0@6d311334
注意最后一行,ServiceA 的实例已经是 CGLIB 代理对象(`
SpringCGLIB0`标识)。这说明三级缓存成功处理了需要 AOP 增强的循环依赖场景。
要真正理解三级缓存,必须深入 Spring 源码,看看关键流程是如何实现的。
AbstractBeanFactory的doGetBean方法是获取 Bean 的入口:
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// 检查单例缓存中是否已有该Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 从缓存中获取Bean实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// 处理原型Bean的循环依赖(Spring不支持)
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 尝试从父容器获取
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 省略代码...
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 省略代码...
// 创建单例Bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 处理原型Bean等其他作用域...
} catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 省略代码...
return adaptBeanInstance(name, bean, requiredType);
}
DefaultSingletonBeanRegistry的getSingleton方法实现了从三级缓存获取 Bean 的逻辑:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 先从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
// 如果一级缓存没有,且Bean正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 2. 从二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果二级缓存没有,且允许早期引用
if (singletonObject == null && allowEarlyReference) {
// 3. 从三级缓存获取工厂对象
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 通过工厂获取Bean实例
singletonObject = singletonFactory.getObject();
// 移到二级缓存,三级缓存移除
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这个方法清晰地体现了三级缓存的查询顺序:一级 → 二级 → 三级。
DefaultSingletonBeanRegistry的另一个getSingleton重载方法负责创建单例 Bean:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 标记Bean开始创建
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
// 调用工厂方法创建Bean(会调用createBean)
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException ex) {
// 省略异常处理...
} finally {
// 取消创建标记
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到一级缓存
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
在AbstractAutowireCapableBeanFactory的doCreateBean方法中,会在 Bean 实例化后暴露早期引用:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 实例化Bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 省略代码...
// 处理单例Bean的循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 添加到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 初始化Bean实例
Object exposedObject = bean;
try {
// 属性填充(依赖注入)
populateBean(beanName, mbd, instanceWrapper);
// 初始化Bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
// 省略异常处理...
}
// 省略代码...
return exposedObject;
}
当 Bean 完全初始化后,会被添加到一级缓存:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加到一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 从三级缓存移除
this.singletonFactories.remove(beanName);
// 从二级缓存移除
this.earlySingletonObjects.remove(beanName);
// 记录已注册的单例
this.registeredSingletons.add(beanName);
}
}
虽然三级缓存很强大,但并非所有循环依赖场景都能解决。
如果循环依赖发生在构造方法层面,Spring 无法解决:
@Service
public class ServiceA {
private final ServiceB serviceB;
// 构造方法依赖ServiceB
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
// 构造方法依赖ServiceA
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
这种情况下,启动会抛出BeanCurrentlyInCreationException。因为构造方法调用是实例化的前提,而实例化完成后才会暴露到三级缓存。
解决方案:
@Lazy注解延迟初始化其中一个依赖@Service
public class ServiceA {
private final ServiceB serviceB;
// 使用@Lazy延迟初始化ServiceB
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Spring 不支持原型 Bean(prototype)的循环依赖。因为原型 Bean 每次获取都会创建新实例,无法缓存。
@Service
@Scope("prototype")
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
@Scope("prototype")
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
这种情况下,Spring 会抛出BeanCurrentlyInCreationException。
解决方案:
Spring 提供了日志支持,通过设置org.springframework.beans.factory的日志级别为 DEBUG,可以看到 Bean 的创建和依赖注入过程。
也可以使用 Spring Boot Actuator 的beans端点,查看 Bean 之间的依赖关系:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
访问http://localhost:8080/actuator/beans即可查看详细的 Bean 依赖信息。
循环依赖往往暗示着设计上的问题,可能是职责划分不清导致的。例如,两个紧密耦合的类可能应该合并为一个类,或者通过引入第三个类来解耦。
最佳实践:
三级缓存仅适用于单例(singleton)Bean。其他作用域的 Bean 处理方式不同:
Spring 的三级缓存机制是解决循环依赖问题的优雅方案,它的设计体现了以下思想:
BeanPostProcessor等扩展点,使缓存机制具有良好的扩展性,能够处理 AOP 等复杂场景。理解三级缓存不仅能帮助我们解决实际开发中的循环依赖问题,更能让我们学习到 Spring 框架的设计哲学。在日常开发中,我们应该:
Spring 的三级缓存是其内部机制的精华之一,深入理解它,能让我们在使用 Spring 框架时更加得心应手,写出更高质量的代码。
类名 | 核心方法 | 作用 |
|---|---|---|
DefaultSingletonBeanRegistry | getSingleton(String beanName) | 从三级缓存获取单例 Bean |
DefaultSingletonBeanRegistry | addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) | 添加到三级缓存 |
DefaultSingletonBeanRegistry | addSingleton(String beanName, Object singletonObject) | 添加到一级缓存 |
AbstractAutowireCapableBeanFactory | doCreateBean | 创建 Bean 并暴露早期引用 |
AbstractAutowireCapableBeanFactory | getEarlyBeanReference | 获取早期 Bean 引用(可能创建代理) |
AbstractBeanFactory | doGetBean | 获取 Bean 的入口方法 |
通过这个速查表,你可以快速定位到三级缓存相关的核心代码,方便深入学习和查阅。