问题引入

2023 年春,某团队将一个单体应用拆分为微服务,新服务启动时频繁报错 BeanCurrentlyInCreationException

复制代码
Error creating bean with name 'orderService':
Bean with name 'paymentService' has been injected into other beans
[paymentService] in its raw version as part of a circular reference,
but has eventually been wrapped (for example by a proxy).

技术团队排查后发现,OrderService 通过构造器注入 PaymentServicePaymentService 又通过构造器注入 OrderService。开发者的第一反应是"循环依赖不是 Spring 自动解决的吗?"——但构造器循环依赖和 Setter 循环依赖的待遇完全不同。

这不是个例。Spring 面试几乎必从"什么是 IOC"开始,但追问下去:

  • Bean 是怎么从 class 文件变成容器中的对象的?每一步做了什么?
  • 循环依赖怎么解决的?三级缓存是哪三级?分别存了什么?
  • 为什么用三级缓存,两级行不行?
  • @Autowired 和 @Resource 有什么区别?构造器注入和 Setter 注入哪个更好?
  • prototype 作用域的 Bean 为什么不能用 @PreDestroy?

本文从 Bean 的生命周期出发,穿透 IOC 容器的每一个环节,揭示循环依赖的解决机制与三级缓存的设计哲学。

核心概念

1. IOC 与 DI:控制反转和依赖注入

**IOC(Inversion of Control,控制反转)**是一种设计思想——对象的创建和依赖关系的管理从"对象自己负责"反转给"外部容器负责"。

**DI(Dependency Injection,依赖注入)**是 IOC 的实现方式,有三种注入方式:

注入方式 示例 优点 缺点
构造器注入 public OrderService(PaymentService ps) 依赖明确、不可变、循环依赖可检测 参数多时代码冗长
Setter 注入 setPaymentService(PaymentService ps) 可选依赖、灵活 对象可能处于不完整状态
字段注入 @Autowired private PaymentService ps 简洁 隐藏依赖、测试困难、无法保证不可变性

Spring 官方从 4.x 开始推荐构造器注入,SpringBoot 的 spring-boot-starter 甚至会在检测到字段注入时打印警告。

2. BeanDefinition:Bean 的设计图纸

BeanDefinition 不是 Bean 本身,而是 Bean 的"元信息描述",Spring 容器先读取配置生成 BeanDefinition,再根据图纸创建 Bean。

复制代码
配置来源                    BeanDefinition                    Bean 实例
┌──────────┐              ┌──────────────────┐              ┌──────────┐
│ XML配置   │  解析        │ beanClass:       │  创建        │ Order    │
│ @Component│ ──────────▶  │   OrderService   │ ──────────▶ │ Service  │
│ @Bean     │              │ scope: singleton │              │ 实例     │
│           │              │ lazyInit: false  │              │          │
└──────────┘              │ dependsOn: []    │              └──────────┘
                          └──────────────────┘

读图导引:配置来源(XML、注解、Java Config)被解析为 BeanDefinition,BeanDefinition 是"图纸",最终根据图纸创建 Bean 实例。注意一张图纸可以创建多个实例(如 prototype 作用域)。

核心属性:

  • beanClass:Bean 的全限定类名
  • scope:作用域(singleton、prototype 等)
  • lazyInit:是否延迟初始化
  • dependsOn:显式依赖的 Bean 名称
  • autowireMode:自动装配模式(byType、byName、constructor)
  • initMethodName / destroyMethodName:自定义初始化和销毁方法

3. BeanFactory vs ApplicationContext

特性 BeanFactory ApplicationContext
本质 基础 IOC 容器 高级 IOC 容器
加载方式 延迟加载(getBean 时才创建) 预加载(refresh 时创建所有单例)
事件发布 不支持 支持(ApplicationEvent)
资源加载 不支持 支持(ResourceLoader)
国际化 不支持 支持(MessageSource)
AOP 集成 需手动配置 原生支持

ApplicationContext 继承自 BeanFactory,并扩展了更多企业级功能。生产环境几乎总是使用 ApplicationContext(如 AnnotationConfigApplicationContextClassPathXmlApplicationContext)。

4. Bean 作用域

作用域 说明 适用场景
singleton(默认) 每个 Spring 容器只有一个实例 无状态 Service、DAO
prototype 每次请求创建新实例 有状态对象(如购物车)
request 每个 HTTP 请求一个实例 Web 应用中的请求级数据
session 每个 HTTP Session 一个实例 用户登录信息
application 每个 ServletContext 一个实例 全局配置
websocket 每个 WebSocket 连接一个实例 实时通信

prototype 的陷阱:Spring 只负责创建 prototype Bean,不管理其生命周期。如果 prototype Bean 依赖了 singleton Bean,singleton Bean 持有的 prototype Bean 引用不会更新——每次注入的还是同一个 prototype 实例。要解决这个问题需要使用 ObjectFactory@Lookup 方法。

原理分析

1. Bean 生命周期完整链路

复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│                         Spring Bean 生命周期                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ① 实例化          ② 属性填充          ③ 初始化                         │
│  Instantiation    Population        Initialization                      │
│                                                                         │
│  ┌─────────┐     ┌───────────┐     ┌─────────────────────────────────┐  │
│  │ 反射调用 │     │ @Autowired │     │ BeanNameAware.setBeanName()      │  │
│  │ 构造器   │────▶│ 依赖注入   │────▶│ BeanFactoryAware.setBeanFactory()│  │
│  │         │     │           │     │ ApplicationContextAware...       │  │
│  └─────────┘     └───────────┘     ├─────────────────────────────────┤  │
│                                     │ BeanPostProcessor.               │  │
│                                     │   postProcessBeforeInit()       │  │
│                                     ├─────────────────────────────────┤  │
│                                     │ @PostConstruct                   │  │
│                                     │ InitializingBean.afterProperties()│ │
│                                     │ init-method                      │  │
│                                     ├─────────────────────────────────┤  │
│                                     │ BeanPostProcessor.               │  │
│                                     │   postProcessAfterInit()        │  │
│                                     │   ← AOP 代理在此创建            │  │
│                                     └─────────────────────────────────┘  │
│                                                                          │
│  ④ 使用                         ⑤ 销毁                                  │
│  In Use                        Destruction                               │
│                                     │                                    │
│                                     ▼                                    │
│                              @PreDestroy                                 │
│                              DisposableBean.destroy()                    │
│                              destroy-method                              │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

读图导引:生命周期分五大阶段——实例化(反射创建对象)、属性填充(依赖注入)、初始化(Aware 回调 + 前后处理器 + 初始化方法)、使用、销毁。重点关注初始化阶段,这是 AOP 代理创建和自定义逻辑介入的关键时机。

详细步骤解析

① 实例化(Instantiation)

Spring 通过反射调用 Bean 的构造器创建对象:

java 复制代码
// 简化的创建逻辑
Object bean = beanClass.getDeclaredConstructor().newInstance();

此时对象已创建,但属性都还是默认值(null、0、false)。

② 属性填充(Population)

Spring 解析 BeanDefinition 中的属性定义,通过反射注入依赖:

java 复制代码
// 简化的依赖注入
for (PropertyValue pv : beanDefinition.getPropertyValues()) {
    Field field = beanClass.getDeclaredField(pv.getName());
    field.setAccessible(true);
    field.set(bean, resolveValue(pv.getValue()));  // 解析并注入值
}

@Autowired 的解析由 AutowiredAnnotationBeanPostProcessor 完成,它会扫描字段和方法上的 @Autowired 注解,从容器中查找匹配的 Bean 注入。

③ 初始化(Initialization)

初始化阶段是 Bean 真正"就绪"前的准备过程,分为三个子阶段:

Aware 接口回调:如果 Bean 实现了 Aware 接口,Spring 会注入容器相关信息:

  • BeanNameAware.setBeanName():注入 Bean 的名称
  • BeanFactoryAware.setBeanFactory():注入 BeanFactory
  • ApplicationContextAware.setApplicationContext():注入 ApplicationContext

BeanPostProcessor 前置处理:所有注册的 BeanPostProcessor.postProcessBeforeInitialization() 被调用。

初始化方法:三种方式按顺序执行:

  1. @PostConstruct(JSR-250 注解,由 CommonAnnotationBeanPostProcessor 处理)
  2. InitializingBean.afterPropertiesSet()(Spring 接口)
  3. 自定义 init-method(XML 或 @Bean(initMethod) 配置)

BeanPostProcessor 后置处理postProcessAfterInitialization() 被调用。这是AOP 代理创建的关键时机——如果 Bean 需要被代理,AbstractAutoProxyCreator 在此生成代理对象替代原始 Bean。

④ 使用(In Use)

Bean 被放入 singletonObjects(一级缓存),可以被其他 Bean 注入或从容器中获取。

⑤ 销毁(Destruction)

容器关闭时,按逆序调用销毁方法:

  1. @PreDestroy(JSR-250)
  2. DisposableBean.destroy()(Spring 接口)
  3. 自定义 destroy-method

2. 循环依赖与三级缓存

什么是循环依赖

java 复制代码
@Component
public class OrderService {
    @Autowired
    private PaymentService paymentService;  // OrderService 依赖 PaymentService
}

@Component
public class PaymentService {
    @Autowired
    private OrderService orderService;      // PaymentService 依赖 OrderService
}

两个 Bean 互相依赖,形成闭环。如果没有任何特殊处理,创建 A 需要 B,创建 B 需要 A,永无止境。

三级缓存的结构

java 复制代码
// DefaultSingletonBeanRegistry 中的三个缓存
/** 一级缓存:成品 Bean,完全初始化好的单例对象 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 二级缓存:半成品 Bean,已实例化但未填充属性的早期引用 */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

/** 三级缓存:Bean 工厂,用于生成早期引用(可能包含 AOP 代理逻辑) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

三级缓存解决循环依赖的过程

复制代码
创建 OrderService(A)                    创建 PaymentService(B)
    │                                          │
    ▼                                          ▼
┌─────────────┐                         ┌─────────────┐
│ 1. 实例化 A  │                         │ 1. 实例化 B  │
│    (new A)   │                         │    (new B)   │
└──────┬──────┘                         └──────┬──────┘
       │                                        │
       ▼                                        ▼
┌─────────────┐                         ┌─────────────┐
│ 2. 将 A 放入 │                         │ 2. 将 B 放入 │
│ 三级缓存      │                         │ 三级缓存      │
│ (ObjectFactory)│                        │ (ObjectFactory)│
└──────┬──────┘                         └──────┬──────┘
       │                                        │
       ▼                                        ▼
┌─────────────┐                         ┌─────────────┐
│ 3. 填充属性   │                         │ 3. 填充属性   │
│    需要 B    │◀────────────────────────│    需要 A    │
└──────┬──────┘                         └──────┬──────┘
       │                                        │
       │ 4. 从三级缓存获取 B                     │ 4. 从三级缓存获取 A
       │    (调用工厂创建)                       │    (调用工厂获取早期引用)
       │◀───────────────────────────────────────│
       │                                        │
       ▼                                        ▼
┌─────────────┐                         ┌─────────────┐
│ 5. B 完成创建 │                         │ 5. A 拿到 B 的早期引用
│    进入一级缓存│                        │    继续填充属性
└──────┬──────┘                         └──────┬──────┘
       │                                        │
       ▼                                        ▼
┌─────────────┐                         ┌─────────────┐
│ 6. A 继续初始化│                         │ 6. A 完成创建 │
│    使用完整的 B│                        │    进入一级缓存│
└─────────────┘                         └─────────────┘

读图导引:关键在第 4 步——B 在填充属性时需要 A,此时 A 还在创建中(未完成初始化),Spring 从三级缓存中取出 A 的 ObjectFactory,调用它获取 A 的"早期引用"(early reference)注入给 B。B 完成后,A 继续初始化,最终拿到的是完整的 B。

为什么必须是三级缓存?两级行不行?

这是面试的终极追问。

如果只使用两级缓存(去掉三级缓存,只用一级 + 二级)

java 复制代码
// 假设只有一级和二级
// 实例化 A 后,直接将 A 的早期引用放入二级缓存
earlySingletonObjects.put("a", a);

问题在于AOP 代理。如果 A 需要被 AOP 代理,那么注入给 B 的应该是 A 的代理对象,而不是原始 A 对象。

在 Spring 中,AOP 代理的创建时机是在 BeanPostProcessor.postProcessAfterInitialization()(初始化后)。也就是说:

  • A 实例化后、初始化前:只有原始对象
  • A 初始化后:可能变成代理对象

如果直接把原始 A 放入二级缓存,B 注入的就是原始 A,而不是代理后的 A。这会导致:B 中注入的 A 和最终容器中的 A 不是同一个对象(一个是原始对象,一个是代理对象)。

三级缓存的设计哲学

java 复制代码
// 三级缓存存的是工厂,不是对象本身
singletonFactories.put(beanName, () -> {
    // 这个工厂在第一次被调用时,会检查是否需要创建代理
    // 如果需要,返回代理后的早期引用
    // 如果不需要,返回原始对象的早期引用
    return getEarlyBeanReference(beanName, mbd, bean);
});

三级缓存中的 ObjectFactory 是一个延迟创建的工厂:

  • 如果没有循环依赖,这个工厂永远不会被调用,AOP 代理在正常的初始化后阶段创建
  • 如果有循环依赖,工厂被提前调用,此时会触发 SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference(),让 AOP 代理有机会提前创建

两级缓存的 workaround:如果去掉三级缓存,直接把 getEarlyBeanReference() 的结果放入二级缓存,会导致所有 Bean 都在实例化后立刻创建 AOP 代理,违背了"代理在初始化后创建"的设计,也可能导致 BeanPostProcessor 的执行顺序问题。

所以:三级缓存是 Spring 为了兼顾循环依赖解决和 AOP 代理正确性而设计的精妙结构

构造器循环依赖:三级缓存也无能为力

java 复制代码
@Component
public class OrderService {
    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {  // 构造器注入
        this.paymentService = paymentService;
    }
}

@Component
public class PaymentService {
    private final OrderService orderService;

    public PaymentService(OrderService orderService) {  // 构造器注入
        this.orderService = orderService;
    }
}

为什么构造器循环依赖无法解决?

因为构造器注入发生在实例化阶段,此时对象还没创建完成,连早期引用都不存在。Spring 尝试创建 A → 发现需要 B → 尝试创建 B → 发现需要 A → A 还在创建中但三级缓存里还没有(因为实例化还没完成)。

解决方案:

  • 改用 Setter 注入或字段注入(让 Spring 用三级缓存解决)
  • 使用 @Lazy 延迟注入(注入的是代理对象,真正使用时才创建目标 Bean)
  • 重构代码,打破循环依赖(推荐,从根本上解决问题)

3. @Autowired 与 @Resource 的底层差异

维度 @Autowired @Resource
来源 Spring 注解 JSR-250 标准注解
匹配规则 先按类型(Type),再按名称 先按名称(Name),再按类型
required 支持 required = false 不支持(可通过 name 指定)
作用位置 构造器、方法、字段、参数 字段、方法

@Autowired 的解析过程

  1. 按类型查找所有匹配的 Bean
  2. 如果只有一个,直接注入
  3. 如果有多个,按字段名/参数名匹配 Bean 名称
  4. 如果还匹配不上,抛出 NoUniqueBeanDefinitionException
  5. 可以用 @Primary 标注首选 Bean,或用 @Qualifier 指定名称

@Resource 的解析过程

  1. 先按 name 属性查找(默认是字段名/方法名)
  2. 如果找不到,再按类型查找

实战/源码

1. Bean 生命周期验证代码

java 复制代码
@Component
public class LifecycleBean implements BeanNameAware, BeanFactoryAware,
        ApplicationContextAware, InitializingBean, DisposableBean {

    public LifecycleBean() {
        System.out.println("① 构造器:实例化");
    }

    @Autowired
    public void setDependency(String dependency) {
        System.out.println("② Setter:属性填充");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("③ BeanNameAware:" + name);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println("④ BeanFactoryAware");
    }

    @Override
    public void setApplicationContext(ApplicationContext context) {
        System.out.println("⑤ ApplicationContextAware");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("⑥ @PostConstruct");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("⑦ InitializingBean.afterPropertiesSet()");
    }

    public void initMethod() {
        System.out.println("⑧ init-method");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("⑨ @PreDestroy");
    }

    @Override
    public void destroy() {
        System.out.println("⑩ DisposableBean.destroy()");
    }

    public void destroyMethod() {
        System.out.println("⑪ destroy-method");
    }
}

输出顺序:

复制代码
① 构造器:实例化
② Setter:属性填充
③ BeanNameAware:lifecycleBean
④ BeanFactoryAware
⑤ ApplicationContextAware
⑥ @PostConstruct
⑦ InitializingBean.afterPropertiesSet()
⑧ init-method
... Bean 就绪 ...
⑨ @PreDestroy
⑩ DisposableBean.destroy()
⑪ destroy-method

2. 三级缓存核心源码解析

java 复制代码
// DefaultSingletonBeanRegistry.java

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 1. 先从一级缓存拿成品 Bean
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 2. 一级没有,从二级缓存拿早期引用
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // 3. 二级也没有,从三级缓存拿工厂
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                // 4. 调用工厂创建早期引用
                singletonObject = singletonFactory.getObject();
                // 5. 提升到二级缓存(从三级移除)
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

关键逻辑

  • 一级缓存 singletonObjects:存完全初始化好的 Bean(成品)
  • 二级缓存 earlySingletonObjects:存已实例化但未初始化的 Bean(半成品),从三级缓存晋升而来
  • 三级缓存 singletonFactories:存工厂,用于生成早期引用(可能包含 AOP 代理逻辑)

3. 循环依赖排查与解决

排查方法

bash 复制代码
# 启动时添加 DEBUG 日志,查看 Bean 创建过程
java -jar app.jar --debug
# 或 application.properties
logging.level.org.springframework.beans.factory.support=DEBUG

解决方案对比

场景 方案 代码
Setter/字段循环依赖 Spring 自动解决(三级缓存) 无需改动
构造器循环依赖 改用 Setter 注入 去掉 final,加 @Autowired Setter
构造器循环依赖 @Lazy 延迟注入 public A(@Lazy B b)
构造器循环依赖 重构打破循环 提取公共逻辑到 C

@Lazy 的原理

java 复制代码
@Component
public class OrderService {
    private final PaymentService paymentService;

    public OrderService(@Lazy PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

@Lazy 注入的是目标 Bean 的代理对象(TargetSource proxy),真正调用方法时才从容器中获取真实 Bean。这样就打破了"创建 A 需要创建 B"的循环——A 只需要 B 的代理即可实例化。

4. prototype 作用域的正确用法

java 复制代码
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ShoppingCart {
    private List<Item> items = new ArrayList<>();
    // ...
}

@Service
public class OrderService {
    // 错误:只会在注入时创建一次 prototype Bean
    @Autowired
    private ShoppingCart cart;

    // 正确:每次使用时获取新的 prototype Bean
    @Autowired
    private ObjectFactory<ShoppingCart> cartFactory;

    public void addItem(Item item) {
        ShoppingCart cart = cartFactory.getObject();  // 每次获取新实例
        cart.add(item);
    }
}

常见问题

Q1:@Autowired 和 @Resource 用哪个?

:优先用 @Autowired(Spring 生态更自然),但两者都可以。关键区别:

  • @Autowired 先按类型匹配,类型冲突时用名称
  • @Resource 先按名称匹配,名称找不到时按类型

如果容器中有多个同类型 Bean(如多个 DataSource),@Autowired 需要配合 @Qualifier,而 @Resource 可以直接用 name 属性:

java 复制代码
@Autowired
@Qualifier("masterDataSource")
private DataSource dataSource;

// 等价于
@Resource(name = "masterDataSource")
private DataSource dataSource;

Q2:构造器注入和 Setter 注入哪个更好?

:Spring 官方推荐构造器注入,原因:

  1. 依赖不可变:字段可以声明为 final,保证对象创建后依赖不会被修改
  2. 依赖明确:构造器参数一目了然,知道 Bean 需要什么
  3. 循环依赖可检测:构造器循环依赖会在启动时直接报错,而不是在运行时才发现
  4. 测试友好:单元测试时可以直接 new Service(mockDep),不需要反射

Setter 注入适用于可选依赖——依赖有默认值,不设置也能正常工作。

字段注入虽然简洁,但隐藏了依赖关系,不利于测试和代码审查,SpringBoot 甚至会在检测到字段注入时打印警告:

复制代码
@Autowired fields are not recommended

Q3:为什么 SpringBoot 2.x 推荐使用构造器注入,但 @Autowired 可以省略?

:Spring 4.3+ 开始,如果 Bean 只有一个构造器@Autowired 可以省略,Spring 会自动用它作为注入构造器。

java 复制代码
@Service
public class OrderService {
    private final PaymentService paymentService;

    // 只有一个构造器,@Autowired 可省略
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

但如果有多个构造器,必须用 @Autowired 标注要用于注入的那个:

java 复制代码
@Service
public class OrderService {
    private PaymentService paymentService;
    private CacheManager cacheManager;

    @Autowired  // 标注这个构造器用于依赖注入
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public OrderService(PaymentService paymentService, CacheManager cacheManager) {
        this.paymentService = paymentService;
        this.cacheManager = cacheManager;
    }
}

Q4:三级缓存中的 ObjectFactory 什么时候会被调用?

:只有在发生循环依赖时才会被提前调用。

正常流程(无循环依赖):

  1. 实例化 A
  2. 将 A 的 ObjectFactory 放入三级缓存
  3. 填充 A 的属性(不需要其他 Bean,或需要的 Bean 已在一级缓存)
  4. 初始化 A(包括 AOP 代理创建)
  5. 将成品 A 放入一级缓存,从三级缓存移除 ObjectFactory

循环依赖流程:

  1. 实例化 A
  2. 将 A 的 ObjectFactory 放入三级缓存
  3. 填充 A 的属性,发现需要 B
  4. 创建 B,B 填充属性时需要 A
  5. 从三级缓存调用 A 的 ObjectFactory,获取 A 的早期引用
  6. B 完成创建,进入一级缓存
  7. A 继续初始化,完成创建,进入一级缓存

Q5:prototype 作用域的 Bean 为什么没有销毁回调?

:因为 Spring 不管理 prototype Bean 的生命周期。

singleton Bean 的销毁由容器统一管理(容器关闭时逐个调用销毁方法),但 prototype Bean 创建后就交给调用者了,Spring 容器不持有它的引用,也不知道它什么时候该销毁。

如果 prototype Bean 需要释放资源,调用者需要自己管理:

java 复制代码
@Component
public class PrototypeBean {
    private Connection connection;

    @PostConstruct
    public void init() {
        connection = dataSource.getConnection();
    }

    // 自定义释放方法,调用者负责调用
    public void release() {
        if (connection != null) {
            connection.close();
        }
    }
}

或者用 DisposableBeanAdapter 注册自定义销毁回调(较复杂,不推荐)。

总结

Spring IOC 容器的核心不是"把对象交给容器管"这句口号,而是 Bean 从 class 到可用对象的完整生命周期管理:

  1. BeanDefinition 是图纸,不是房子:Spring 先解析配置生成 BeanDefinition,再根据图纸创建 Bean,这个分层设计让配置和实例化解耦
  2. Bean 生命周期的关键是初始化阶段:Aware 回调让 Bean 感知容器,BeanPostProcessor 让框架和开发者能在初始化的前后插入逻辑,AOP 代理就在后置处理中创建
  3. 三级缓存是解决循环依赖的精妙设计:一级存成品、二级存半成品、三级存工厂,三级缓存中的 ObjectFactory 不仅提供早期引用,还承担了 AOP 代理提前创建的责任,两级缓存无法同时满足循环依赖和代理正确性
  4. 构造器循环依赖是设计问题,不是框架问题:Spring 不解决构造器循环依赖,因为实例化阶段就需要依赖对象,此时早期引用还不存在。遇到构造器循环依赖应该反思设计,或者用 @Lazy 延迟注入作为临时方案
  5. 注入方式的选择有明确的最佳实践:构造器注入优于 Setter 注入,Setter 注入优于字段注入;构造器注入让依赖明确、不可变、可测试

理解 IOC 容器后,下一个追问自然指向 AOP——因为 AOP 代理正是在 Bean 生命周期的 BeanPostProcessor.postProcessAfterInitialization() 中创建的。理解容器是理解 AOP 的前提。