理解 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 获得容器相关的信息:
BeanNameAware→setBeanName(String name):获取自身在容器中的名字BeanFactoryAware→setBeanFactory(BeanFactory beanFactory):获取所在容器的引用ApplicationContextAware→setApplicationContext(ApplicationContext ctx):获取完整的应用上下文(仅对ApplicationContext中的 Bean 有效)- 其他专门的 Aware:如
ResourceLoaderAware、EnvironmentAware、MessageSourceAware等
通过这些接口,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 完全准备就绪前执行自定义的初始化逻辑,三种方式按以下顺序执行:
@PostConstruct注解的方法
- 由 CommonAnnotationBeanPostProcessor 驱动,使用标准注解,推荐此方式。
- 适合资源准备、连接建立、预热缓存等逻辑。
InitializingBean接口的afterPropertiesSet()方法
- 实现该接口的 Bean 在执行完
@PostConstruct后调用。 - 由于与 Spring 接口耦合,现代开发中不常使用,但很多内部组件仍采用此方式。
- 自定义
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 初始化后处理:生成代理的关键时刻
初始化完成后,容器再次调用所有 BeanPostProcessor 的 postProcessAfterInitialization(Object bean, String beanName) 方法。这一步是 Spring AOP 实现的核心——如果你的 Bean 需要被代理(例如被 @Transactional、@Async、@Cacheable 或自定义切面拦截),代理对象就是在这里被创建并返回的。
从这里开始,容器中持有的引用可能是代理对象,而不再是原始 Bean 实例。这也是为什么有时候直接使用
this调用方法不会触发 AOP 增强的原因:this指向原始对象,而不是代理。
如果每个调用都会经过代理,Spring 就可以在方法执行前后插入事务、日志、权限检查等横切逻辑,而开发者完全无需感知。
4.1.7 就绪:Bean 进入可用状态
经过上述步骤,Bean 已经完全就绪,被放入单例池(对于 singleton 作用域)等待着被注入到其他 Bean,或者直接被应用调用。
当所有非懒加载的单例 Bean 都完成初始化后,Spring 容器会触发一些应用层级的事件,比如 ContextRefreshedEvent、ApplicationReadyEvent。开发者可以通过 @EventListener 监听这些事件,完成应用启动后的收尾工作:
@Component
public class StartupChecker {
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
System.out.println("所有Bean已就绪,应用可以对外提供服务");
}
}
4.1.8 销毁:有序的资源释放
当容器关闭时(例如 context.close() 被调用,或 JVM 关闭钩子触发),Spring 会依次销毁所有单例 Bean。销毁的顺序与初始化的顺序类似:
@PreDestroy注解的方法DisposableBean接口的destroy()方法- 自定义
destroy-method(如@Bean(destroyMethod="close"))
这些回调常用于释放连接池、关闭文件流、注销注册中心、取消定时任务等资源清理工作。如果不显式指定 destroy-method,Spring Boot 还会根据常见命名(如 close、shutdown)自动推导销毁方法,减少配置遗漏。
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调用为什么穿透了增强逻辑。 - 优雅关闭:利用
@PreDestroy或destroy-method确保外部资源被安全释放。
可以说,Spring 的 Bean 并不是一个生硬的静态对象,而是一个经历了精心编排的生命过程的组件。掌握了这个过程的细节,才能真正做到“用 Spring 如指臂使”,而不是停留在注解上盲目配置。