Bean 的三级缓存
Spring 的三级缓存是解决循环依赖的核心机制。
常见的 bean 依赖场景

循环依赖问题
java
@Component
public class CycleServiceA {
@Autowired
private CycleServiceB serviceB; // A依赖B
}
@Component
public class CycleServiceB {
@Autowired
private CycleServiceA serviceA; // B依赖A,形成循环依赖
}
// ❌ 问题:先创建 A 需要 B,先创建 B 需要 A,死锁!以上情况为属性注入循环依赖, IOC 容器可能会解决掉,在配置文件中配置如下:
yaml
spring:
main:
allow-circular-references: trueSpring 内部的标志位,默认允许。可以通过 AbstractAutowireCapableBeanFactory 类中的 allowCircularReferences 查看。
也可以明确禁止:
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 方式1:禁用循环依赖(推荐用于生产环境)
SpringApplication app = new SpringApplication(Application.class);
app.setAllowCircularReferences(false); // 明确禁止
app.run(args);
// 应用启动时会检查循环依赖,发现则直接报错
// 错误信息:The dependencies of some of the beans in the application context form a cycle
}
}注入形式可以还可以通过构造函数、setter 注入。通过构造器函数进行依赖,Spring IOC 是无法解决循环注入依赖的。
三级缓存
java
public class DefaultSingletonBeanRegistry {
// 一级缓存:完全初始化好的单例 Bean,功能完备的 Bean,应用程序需要使用的时候,需要从这个 Map 里面调用
// Key: beanName, Value: 完整的Bean实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:提前暴露的 Bean(半成品),实例化之后,没有完成属性注入的 Bean。
// 存在的意义是提前暴露 Bean,让其他的 Bean 知道有这么一个 Bean 存在于 Spring IOC 中,已经可以使用了。
// Key: beanName, Value: 早期的Bean引用
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存:ObjectFactory工厂
// 创建 Bean 早期引用(或代理对象)的工厂,生成原始对象,对 AOP 进行操作后的代理对象
// 代理对象:在不修改原始对象的情况下,控制对原始对象的访问
// Key: beanName, Value: 创建Bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 正在创建中的Bean名称(Set集合)
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}解决循环依赖

- 1、在创建 Bean A 的时候,将 Bean A 的原始对象,存放在缓存中。这里只是将 A new 出来了,并没有将它注入所需要的依赖。
- 2、Spring IOC 实现依赖注入的时候,发现需要依赖 B
- 3、创建 B 对象,实例化
- 4、将创建 B 实例化之后的原始对象放在缓存里
- 5、在对 B 进行依赖注入时,发现需要 A
- 6、从缓存中取出已经被实力化的 A
- 7、对 B 完成依赖注入
- 8、完成 A 的依赖注入
二级缓存是否足够了
A 和 B 互相依赖,通过缓存的方式解决了循环依赖的问题,没有使用到第三级缓存的。
第三级缓存 singletonFactories 是为了处理 Spring AOP 的中的代理对象。
没有使用 AOP 的操作, B 和 A 完成创建后,注入的最终对象是同一个对象, 如果并 A 方法中有 AOP 的操作,A 的原始对象的复值给 B 时,A 会进行 AOP 操作,产生一个代理对象
怎么解决工程中存在的循环依赖
- 1、做好设计和规划,尽量避免多个Bean的功能之间存在交叉,遵循职责独立
- 2、使用 Abstract Bean,公用的功能定义在其中
- 3、剥离出『中间 Bean』,其他 Bean 对其依赖注入
剑鸣秋朔