1. Spring三级缓存与循环依赖的深度解析
1.1 循环依赖的本质与解决方案
在Spring框架中,循环依赖是指两个或多个Bean相互依赖形成的闭环关系。比如BeanA依赖BeanB,同时BeanB又依赖BeanA。这种场景在构造器注入模式下会直接导致容器启动失败,因为Spring无法同时满足两个Bean的实例化条件。
Spring解决这个问题的核心思路是"提前暴露引用"。通过将尚未完全初始化的Bean提前暴露给其他依赖方,打破实例化与属性注入之间的强顺序约束。这种机制具体通过三级缓存实现:
java复制// Spring容器中实际的三级缓存结构
public class DefaultSingletonBeanRegistry {
// 一级缓存:完整Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存:早期引用
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
// 三级缓存:对象工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
}
1.2 三级缓存的工作机制详解
1.2.1 缓存层级与职责划分
一级缓存(singletonObjects):
- 存储完全初始化好的Bean实例
- 所有属性注入完成
- 所有后置处理器处理完毕
- 可以直接被其他Bean使用
二级缓存(earlySingletonObjects):
- 存储"半成品"Bean的早期引用
- 仅完成实例化,属性可能未完全注入
- 主要解决循环依赖问题
三级缓存(singletonFactories):
- 存储ObjectFactory工厂对象
- 可以生成Bean的早期引用(包括AOP代理)
- 延迟创建实际对象,减少内存占用
1.2.2 典型处理流程示例
以BeanA依赖BeanB,BeanB依赖BeanA为例:
- 开始创建BeanA:
- 实例化BeanA(调用构造函数)
- 将BeanA的ObjectFactory放入三级缓存
- 处理BeanA的属性注入:
- 发现需要注入BeanB
- 开始创建BeanB:
- 实例化BeanB
- 将BeanB的ObjectFactory放入三级缓存
- 处理BeanB的属性注入:
- 发现需要注入BeanA
- 从三级缓存获取BeanA的ObjectFactory
- ObjectFactory.getObject()触发AOP代理创建(如果需要)
- 将生成的代理对象放入二级缓存
- BeanB完成属性注入
- BeanB初始化完成,放入一级缓存
- BeanA继续完成属性注入(此时注入的是BeanB的完整实例)
- BeanA初始化完成,放入一级缓存
关键点:三级缓存中的ObjectFactory只有在发生循环依赖时才会被调用,避免了不必要的代理对象创建。
1.3 性能优化与内存管理
三级缓存设计体现了典型的空间换时间思想:
-
按需创建代理:
- 只有发生循环依赖时才通过ObjectFactory创建代理
- 无循环依赖的Bean不会触发代理创建
- 避免大量未使用的代理对象占用内存
-
缓存分层管理:
- 一级缓存存储完整对象,查询频率最高
- 二级缓存作为过渡存储,生命周期短
- 三级缓存存储轻量级工厂,占用资源少
-
避免重复创建:
- 通过早期引用保证整个依赖链中使用同一个代理对象
- 防止因多次调用newProxyInstance()产生不同代理实例
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Spring AOP核心机制剖析
2.1 AOP在Spring体系中的定位
AOP(面向切面编程)是Spring框架的三大核心特性之一(另外两个是IoC和事务管理)。它通过代理机制实现了横切关注点的模块化,典型应用包括:
- 声明式事务管理(@Transactional)
- 安全控制(Spring Security)
- 日志记录
- 性能监控
- 异常处理
2.2 AOP核心组件详解
2.2.1 核心概念五要素
-
连接点(JoinPoint):
- 程序执行过程中的特定点
- Spring AOP仅支持方法执行级别的连接点
- 包含丰富的上下文信息(方法、参数、目标对象等)
-
切点(Pointcut):
- 匹配连接点的谓词表达式
- 常用表达式示例:
java复制// 匹配Service包下所有方法 @Pointcut("execution(* com.example.service.*.*(..))") // 匹配带有特定注解的方法 @Pointcut("@annotation(com.ex
