人人都会AI编程

4.1 Bean 完整生命周期:实例化 → 属性填充 → 初始化 → 就绪 → 销毁

更新时间:2026-07-10

理解 Bean 在 Spring 容器中从无到有、再到消亡的完整过程,是掌握 Spring 核心机制的关键一环。表面上只需一个 @Component 注解,容器就能自动管理一切;但实际上,Bean 的创建经历了多个精心设计的阶段,每一步都提供了灵活的扩展点,让开发者可以在适当的时机介入定制。

4.1.1 生命周期全貌

一个 Spring Bean 的完整生命周期可以简化为五个阶段:

实例化 → 属性填充 → 初始化 → 就绪 → 销毁

流程虽简单,但 Spring 在每一阶段前后都埋入了大量的回调接口和处理器,使得整个生命周期远比看上去丰富。真正理解这些阶段的作用和时机,是实现定制化功能(比如在 Bean 启动后检查依赖、在关闭前释放资源)的基础。

下图是 Spring 容器管理 Bean 的详细生命周期流程图(文字描述):

1️ 实例化 (Instantiation)
   └─ 通过构造器或工厂方法创建 Bean 实例
2️ 属性填充 (Populate Properties)
   ├─ 注入依赖(@Autowired, @Value 等)
   └─ 调用各种 Aware 接口(BeanNameAware, BeanFactoryAware, ApplicationContextAware 等)
3️ 初始化前 (PostProcessBeforeInitialization)
   └─ 执行所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法
4️ 初始化 (Initialization)
   ├─ @PostConstruct 方法
   ├─ InitializingBean 接口的 afterPropertiesSet()
   └─ 自定义 init-method
5️ 初始化后 (PostProcessAfterInitialization)
   └─ 执行所有 BeanPostProcessor 的 postProcessAfterInitialization 方法(AOP 代理常在此生成)
6️ 就绪 (Ready)
   └─ Bean 准备好被应用使用,容器启动完成后触发 @EventListener(ApplicationReadyEvent)
7️ 销毁 (Destruction)
   ├─ @PreDestroy 方法
   ├─ DisposableBean 接口的 destroy()
   └─ 自定义 destroy-method

接下来,我们深入每个阶段,结合具体场景说明其中的关键组件和实际价值。

4.1.2 实例化:从无到有的第一步

容器启动后,首先是读取 BeanDefinition(Bean 的元数据),然后通过反射调用构造器或工厂方法创建 Bean 的原生实例。此时 Bean 只是一个空壳,所有依赖尚未注入,任何 @Autowired 字段都还是 null

值得一提的是,Spring 支持多种实例化方式:

  • 通过 Class 构造器(默认,如 new MyService()
  • 通过静态工厂方法(factory-method
  • 通过实例工厂方法(factory-bean + factory-method
  • 使用 @Bean 标注的方法(Java Config 中)

不论哪种方式,结果上都是得到一个刚刚分配的对象,属性值都处于默认状态。

4.1.3 属性填充:依赖注入与 Aware 回调

实例化完成后,Spring 会根据 BeanDefinition 中的属性配置进行填充。对于注解驱动的开发,主要完成两件事:

1. 依赖注入(Dependency Injection)

Spring 会扫描 Bean 中的 @Autowired@Value@Inject@Resource 等注解,将对应的依赖注入进来。注入的顺序大致是:先处理 @Autowired 字段和方法,再处理 @Value 属性占位符。

如果依赖的是另一个还没有创建的 Bean,容器会先创建那个 Bean,形成“依赖图”的递归解析。这也是循环依赖可能引发问题的阶段。

2. Aware 接口回调

属性填充之后、初始化之前,Spring 会检查当前 Bean 是否实现了一系列 Aware 接口,并回调相应方法,让 Bean 获得容器相关的信息:

  • BeanNameAwaresetBeanName(String name):获取自身在容器中的名字
  • BeanFactoryAwaresetBeanFactory(BeanFactory beanFactory):获取所在容器的引用
  • ApplicationContextAwaresetApplicationContext(ApplicationContext ctx):获取完整的应用上下文(仅对 ApplicationContext 中的 Bean 有效)
  • 其他专门的 Aware:如 ResourceLoaderAwareEnvironmentAwareMessageSourceAware

通过这些接口,Bean 可以拿到基础设施的引用,用来手动获取其他 Bean、读取环境变量、访问资源等。虽然实际业务代码中很少需要实现这些接口,但在开发框架级组件(如自定义工具类、中间件集成)时非常常见。

4.1.4 初始化前处理:BeanPostProcessor 的舞台

属性填充和 Aware 回调之后,Bean 并没有立刻进入初始化方法,而是先经历一轮 Bean 后置处理器 的处理。

Spring 容器会遍历所有已注册的 BeanPostProcessor,依次调用它们的 postProcessBeforeInitialization(Object bean, String beanName) 方法。此时 Bean 已经具备了所有依赖,但尚未执行自定义初始化逻辑。

这个阶段是框架内部大量特性发挥作用的黄金窗口,例如:

  • ApplicationContextAwareProcessor 就是通过这种方式回调 Aware 接口的
  • CommonAnnotationBeanPostProcessor 会识别 @PostConstruct 注解并安排后续执行
  • Spring 的代理逻辑(如 AbstractAutoProxyCreator)会在此处或初始化后生成代理对象

开发者可以自定义 BeanPostProcessor,在此时对特定 Bean 进行包装、收集元数据或额外处理,这是实现框架扩展的利器。

4.1.5 初始化:业务级初始化逻辑

经过前置处理后,Bean 终于进入“初始化”阶段。Spring 允许开发者在 Bean 完全准备就绪前执行自定义的初始化逻辑,三种方式按以下顺序执行:

  1. @PostConstruct 注解的方法
  • 由 CommonAnnotationBeanPostProcessor 驱动,使用标准注解,推荐此方式。
  • 适合资源准备、连接建立、预热缓存等逻辑。
  1. InitializingBean 接口的 afterPropertiesSet() 方法
  • 实现该接口的 Bean 在执行完 @PostConstruct 后调用。
  • 由于与 Spring 接口耦合,现代开发中不常使用,但很多内部组件仍采用此方式。
  1. 自定义 init-method
  • 通过 XML 或 @Bean(initMethod="init") 指定的方法,在上述两步之后调用。
  • 提供一种不依赖注解或接口的初始化回调方式,适合在 Java Config 中重用第三方类。

一个典型的例子是配置数据库连接池的 DataSource

@Bean(initMethod = "getConnection") // 立即获取连接以测试
public DataSource dataSource() {
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
    ...
    return new HikariDataSource(config);
}

4.1.6 初始化后处理:生成代理的关键时刻

初始化完成后,容器再次调用所有 BeanPostProcessorpostProcessAfterInitialization(Object bean, String beanName) 方法。这一步是 Spring AOP 实现的核心——如果你的 Bean 需要被代理(例如被 @Transactional@Async@Cacheable 或自定义切面拦截),代理对象就是在这里被创建并返回的。

从这里开始,容器中持有的引用可能是代理对象,而不再是原始 Bean 实例。这也是为什么有时候直接使用 this 调用方法不会触发 AOP 增强的原因:this 指向原始对象,而不是代理。

如果每个调用都会经过代理,Spring 就可以在方法执行前后插入事务、日志、权限检查等横切逻辑,而开发者完全无需感知。

4.1.7 就绪:Bean 进入可用状态

经过上述步骤,Bean 已经完全就绪,被放入单例池(对于 singleton 作用域)等待着被注入到其他 Bean,或者直接被应用调用。

当所有非懒加载的单例 Bean 都完成初始化后,Spring 容器会触发一些应用层级的事件,比如 ContextRefreshedEventApplicationReadyEvent。开发者可以通过 @EventListener 监听这些事件,完成应用启动后的收尾工作:

@Component
public class StartupChecker {
    @EventListener(ApplicationReadyEvent.class)
    public void onReady() {
        System.out.println("所有Bean已就绪,应用可以对外提供服务");
    }
}

4.1.8 销毁:有序的资源释放

当容器关闭时(例如 context.close() 被调用,或 JVM 关闭钩子触发),Spring 会依次销毁所有单例 Bean。销毁的顺序与初始化的顺序类似:

  1. @PreDestroy 注解的方法
  2. DisposableBean 接口的 destroy() 方法
  3. 自定义 destroy-method(如 @Bean(destroyMethod="close")

这些回调常用于释放连接池、关闭文件流、注销注册中心、取消定时任务等资源清理工作。如果不显式指定 destroy-method,Spring Boot 还会根据常见命名(如 closeshutdown)自动推导销毁方法,减少配置遗漏。

4.1.9 完整示例:跟踪生命周期实录

下面用一个精简的 Spring Boot 示例来直观展示各阶段的执行顺序:

@Component
public class LifecycleBean implements BeanNameAware, InitializingBean, DisposableBean {

    public LifecycleBean() {
        System.out.println("1. 实例化");
    }

    @Autowired
    public void setSomeDependency(OtherBean dep) {
        System.out.println("2. 属性注入");
    }

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

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

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

    @Bean(initMethod = "customInit")
    public void customInit() {
        System.out.println("6. customInit-method");
    }

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

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

    @Bean(destroyMethod = "customDestroy")
    public void customDestroy() {
        System.out.println("9. customDestroy-method");
    }
}

实际输出顺序(中间穿插 BeanPostProcessor 的日志可能会更丰富)基本就是上述编号的顺序。

4.1.10 掌握生命周期的实际意义

理解这个完整链条,至少可以在以下几个场景中派上用场:

  • 需要 Bean 启动后自动执行任务:使用 @PostConstruct 或监听 ApplicationReadyEvent
  • 想在 Bean 创建前修改属性:实现 BeanPostProcessor 的 before 方法。
  • 排查循环依赖:知道注入发生在属性填充阶段,可以更有针对性地设计依赖拓扑。
  • 调试 AOP 失效问题:明白代理是初始化后生成的,从而理解 this 调用为什么穿透了增强逻辑。
  • 优雅关闭:利用 @PreDestroydestroy-method 确保外部资源被安全释放。

可以说,Spring 的 Bean 并不是一个生硬的静态对象,而是一个经历了精心编排的生命过程的组件。掌握了这个过程的细节,才能真正做到“用 Spring 如指臂使”,而不是停留在注解上盲目配置。