外观
一句话答案
核心注解:@Component/@Service/@Repository(Bean 注册)、@Autowired(注入)、@Transactional(事务)、@Configuration+@Bean(配置)。
核心要点
循环依赖场景:
java
@Component
class A {
@Autowired B b; // A 依赖 B
}
@Component
class B {
@Autowired A a; // B 依赖 A → 循环!
}三级缓存的数据结构:
java
// 一级缓存:存放完全初始化好的单例 Bean
Map<String, Object> singletonObjects;
// 二级缓存:存放早期暴露的 Bean(已实例化但未完成属性填充)
Map<String, Object> earlySingletonObjects;
// 三级缓存:存放 Bean 的工厂对象(ObjectFactory,用于创建早期 Bean 引用)
Map<String, ObjectFactory<?>> singletonFactories;解决循环依赖的流程:
创建 A:
1. 实例化 A(new A(),此时 a.b = null)
2. 将 A 的工厂放入三级缓存:singletonFactories.put("A", () -> A实例 or AOP代理)
3. 填充 A 的属性,发现需要 B → 触发 B 的创建
创建 B:
1. 实例化 B(new B(),此时 b.a = null)
2. 将 B 的工厂放入三级缓存
3. 填充 B 的属性,发现需要 A
→ 查一级缓存:没有
→ 查二级缓存:没有
→ 查三级缓存:找到 A 的 ObjectFactory → 调用 getObject() 获取 A 的早期引用
→ 将 A 的早期引用放入二级缓存,删除三级缓存中 A 的工厂
→ b.a = A的早期引用(A虽不完整但已有内存地址)
4. B 初始化完成 → 放入一级缓存
回到 A 的属性填充:
a.b = B(完整的B)
4. A 初始化完成 → 放入一级缓存(同时清理二级缓存中 A 的早期引用)为什么需要三级缓存而不是两级?
- 二级缓存中存的是早期暴露的 Bean 引用(可能是原始对象,也可能是 AOP 代理)
- 三级缓存中存的是 ObjectFactory(工厂),通过工厂可以延迟创建 AOP 代理
- 如果 Bean A 需要被 AOP 代理,其他 Bean 注入的应该是代理对象,而不是原始对象
- 三级缓存的 ObjectFactory 负责:如果 A 需要 AOP 代理,则返回代理对象;否则返回原始对象
- 这样保证了循环依赖时注入的始终是正确的(代理/非代理)对象
循环依赖无法解决的情况:
- 构造器注入的循环依赖(实例化阶段就需要依赖,三级缓存来不及介入)
- prototype(原型)Bean 的循环依赖(不缓存,每次创建新实例)
追问与易错
追问方向:
- @Component 和 @Bean 区别?
- @Autowired 和 @Resource 区别?
- @Configuration 和 @Component 区别?
易错点:
- ❌ @Component/@Service/@Repository 完全一样——语义不同
- ❌ @Configuration 不加也行——lite mode 不保证 @Bean 单例
💡 记忆锚点
注解分三组记:注册Bean用@Component/@Service/@Repository(三者功能相同语义不同);注入依赖用@Autowired(按类型)+@Qualifier(按名称);配置类用@Configuration+@Bean(手动注册第三方类)。@Configuration保证@Bean方法返回单例,不加只是lite模式。