Skip to content
基础

一句话答案

核心注解:@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 代理,则返回代理对象;否则返回原始对象
  • 这样保证了循环依赖时注入的始终是正确的(代理/非代理)对象

循环依赖无法解决的情况:

  1. 构造器注入的循环依赖(实例化阶段就需要依赖,三级缓存来不及介入)
  2. 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模式。