Spring 为什么需要三级缓存

🔍 Spring 为什么需要三级缓存?

—— 一文讲清循环依赖与 AOP 的一致性难题

作者: 赫英年
关键词: Spring、三级缓存、循环依赖、AOP、代理、对象一致性


🌰 一、问题场景

@Service
public class UserService {
    @Autowired
    private OrderService orderService;

    @Transactional // 需要事务控制 → 必须通过代理执行
    public void createUserOrder() {
        orderService.createOrder();
    }
}

@Service
public class OrderService {
    @Autowired
    private UserService userService; // 循环依赖
}
  • 存在循环依赖 ✅
  • UserService 标有 @Transactional ✅
  • Spring 正常启动,但事务可能不生效

为什么?


🧱 二、三级缓存是哪三级?

缓存作用
一级:singletonObjects最终成品 Bean
二级:earlySingletonObjects早期引用(原始或代理)
三级:singletonFactories存 ObjectFactory延迟创建早期引用

✅ 三级缓存存的不是对象,而是一个工厂:
() -> getEarlyBeanReference(bean)


🚫 三、如果只有二级缓存(存原始对象)

假设流程如下:

  1. 创建 UserService → 实例化 → 放入二级缓存(原始对象)
  2. OrderService 依赖 UserService → 从二级缓存拿到原始对象
  3. OrderService 持有原始 UserService
  4. UserService 继续初始化 → BeanPostProcessor 创建代理对象
  5. 代理对象放入一级缓存

💡 什么是“代理对象”?它和“原始对象”有什么区别?

  • 原始对象:就是 new UserService() 得到的实例,没有任何增强
  • 代理对象:Spring 通过 AOP(如 CGLIB 或 JDK 动态代理)生成的“包装对象”,它:
    • 持有原始对象
    • 在调用方法前/后自动织入逻辑,比如:
      • 开启事务(@Transactional
      • 缓存读写(@Cacheable
      • 日志记录、权限校验等

🔑 关键:只有通过代理对象调用方法,这些增强逻辑才会生效!


⚠️ 问题爆发:对象“分裂”!

组件持有实例类型说明
OrderService原始 UserService❌ 无代理调用方法 → 直接执行,不走代理
其他组件代理 UserService✅ 有事务调用方法 → 先开启事务,再执行

🔴 后果:

  • OrderService 调用 userService.createUserOrder()
    → 调的是原始对象的方法
    → 不开启事务 → 即使抛异常,数据库操作也不会回滚!
  • 同一个 Bean 有两个版本 → 违反单例原则,系统行为不一致

✅ 四、Spring 的正确解:三级缓存延迟决策

Spring 的精妙设计:

  1. 创建 UserService → 实例化
  2. 将 ObjectFactory 放入三级缓存() -> getEarlyBeanReference(userService)
  3. OrderService 需要 UserService → 调用工厂的 getObject()
  4. 此时执行 getEarlyBeanReference()
    • 判断是否有 @Transactional
    • 需要 → 创建代理
    • 放入二级缓存
  5. OrderService 持有的是代理对象
  6. UserService 后续初始化完成 → 直接使用已有代理 → 放入一级缓存

✅ 最终状态:对象唯一,代理一致

组件持有实例类型
OrderService代理 UserService
其他组件代理 UserService

🎯 无论谁调用,都走代理 → 事务生效


🌟 五、核心结论

问题答案
为什么不能只用二级缓存?会存原始对象,导致 AOP 失效
为什么需要三级缓存?通过 ObjectFactory 延迟决策,按需创建代理
三级缓存的本质?解决“提前暴露”与“AOP一致性”的矛盾
最终目标?保证整个容器中只有一个 Bean 实例(且正确)

💡 六、一句话总结

如果没有三级缓存的“延迟代理”机制,二级缓存只能存原始对象,就会导致“一个 Bean 有两个版本(原始 + 代理)”,造成 AOP 失效。三级缓存通过 ObjectFactory 延迟决策,确保了代理的正确性和对象的唯一性。


📚 参考源码

// SmartInstantiationAwareBeanPostProcessor.java
default Object getEarlyBeanReference(Object bean, String beanName) {
    return bean;
}

AOP 模块(如 AnnotationAwareAspectJAutoProxyCreator)会重写此方法,在需要时返回代理。


适合场景: 技术短分享、团队周会、面试复盘
特点: 重点突出、逻辑清晰、概念明确

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇