@PostConstruct
和 @PreDestroy
是 JSR-250(Dependency Injection for Java)标准中定义的生命周期注解,用于标记 Bean 的初始化后和销毁前方法。Spring 框架完整支持这两个注解,通过它们可以在 Bean 的生命周期关键节点执行自定义逻辑(如资源初始化、清理)。以下从注解定义、源码解析、生命周期执行时机、使用场景及注意事项展开详细说明。
一、注解定义与源码解析
1. @PostConstruct
注解
@PostConstruct
位于 javax.annotation
包(Jakarta EE 9+ 后移至 jakarta.annotation
),其源码定义如下(简化版):
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PostConstruct {
}
关键特性:
- 标记一个方法在 Bean 的依赖注入完成后、初始化完成前执行(即“后构造”阶段)。
- 方法可以是
public
、protected
或private
,但不能是static
或带参数。
2. @PreDestroy
注解
@PreDestroy
同样位于 javax.annotation
包,源码定义如下:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreDestroy {
}
关键特性:
- 标记一个方法在 Bean 的销毁前执行(即“预销毁”阶段)。
- 方法签名要求与
@PostConstruct
一致(非静态、无参数)。
二、生命周期执行时机:Spring 中的调用顺序
在 Spring Bean 的生命周期中,@PostConstruct
和 @PreDestroy
的执行时机严格遵循以下顺序:
1. @PostConstruct
的执行时机
Bean 的生命周期阶段(简化):
实例化 → 依赖注入 → @PostConstruct
方法 → InitializingBean.afterPropertiesSet()
→ 自定义初始化方法 → Bean 就绪
具体流程:
- Spring 通过反射创建 Bean 实例(构造器调用)。
- 注入依赖(如
@Autowired
字段、方法参数)。 - 调用
@PostConstruct
标记的方法(此时 Bean 已完成依赖注入,但未执行其他初始化逻辑)。
2. @PreDestroy
的执行时机
Bean 销毁阶段的顺序:
DisposableBean.destroy()
→ @PreDestroy
方法 → Bean 销毁(从容器中移除)
具体流程:
- 容器触发 Bean 销毁(如应用关闭、手动调用
destroy()
)。 - 调用
@PreDestroy
标记的方法(此时 Bean 仍可使用,但即将被销毁)。 - 若 Bean 实现了
DisposableBean
接口,调用其destroy()
方法。
三、典型使用场景与示例
1. @PostConstruct
:初始化资源
适用于需要在 Bean 就绪后立即执行的初始化操作(如加载配置、建立连接、初始化缓存)。
示例:初始化数据库连接池
@Service
public class DataSourceService {
private DataSource dataSource;
// 依赖注入(假设通过 @Bean 或 @Autowired 注入)
@Autowired
private DataSourceConfig config;
// @PostConstruct 标记初始化方法
@PostConstruct
public void initDataSource() {
// 使用注入的配置初始化连接池(如 HikariCP)
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(config.getUrl());
hikariConfig.setUsername(config.getUsername());
hikariConfig.setPassword(config.getPassword());
this.dataSource = new HikariDataSource(hikariConfig);
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection(); // 初始化后使用
}
}
2. @PreDestroy
:清理资源
适用于需要在 Bean 销毁前释放的资源(如关闭数据库连接、停止定时任务、释放文件句柄)。
示例:关闭数据库连接池
@Service
public class DataSourceService {
private DataSource dataSource;
// 初始化逻辑(同上)
// @PreDestroy 标记清理方法
@PreDestroy
public void closeDataSource() {
if (dataSource != null && dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).close(); // 关闭连接池
System.out.println("数据源已关闭");
}
}
}
3. 组合使用:完整的生命周期管理
结合 @PostConstruct
和 @PreDestroy
实现资源的“初始化-使用-清理”闭环。
示例:缓存服务的生命周期管理
@Service
public class CacheService {
private CacheManager cacheManager;
@PostConstruct
public void initCache() {
// 初始化缓存管理器(如 Caffeine)
this.cacheManager = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
System.out.println("缓存管理器初始化完成");
}
public void put(String key, Object value) {
cacheManager.get(key, k -> value); // 使用缓存
}
@PreDestroy
public void destroyCache() {
if (cacheManager != null) {
cacheManager.invalidateAll(); // 清空缓存
System.out.println("缓存已清空");
}
}
}
四、源码实现细节与关键类
1. CommonAnnotationBeanPostProcessor
Spring 处理 JSR-250 注解(如 @PostConstruct
、@PreDestroy
、@Resource
)的核心类,继承自 InstantiationAwareBeanPostProcessorAdapter
。其关键逻辑如下:
(1)postProcessBeforeInitialization
方法
在 Bean 初始化前(InitializingBean.afterPropertiesSet()
之前),扫描并调用 @PostConstruct
标记的方法。
(2)postProcessBeforeDestruction
方法
在 Bean 销毁前,扫描并调用 @PreDestroy
标记的方法。
2. 执行顺序的底层控制
Spring 通过以下顺序确保生命周期方法的正确调用:
- 构造器调用 → 2. 依赖注入 → 3.
@PostConstruct
方法 → 4.InitializingBean.afterPropertiesSet()
→ 5. 自定义初始化方法 → 6. Bean 就绪 → … → 销毁阶段:DisposableBean.destroy()
→@PreDestroy
方法 → Bean 移除。
五、注意事项与常见问题
1. 方法签名限制
- 方法必须是非静态(
static
方法无法访问实例变量)。 - 方法不能有参数(Spring 无法传递参数)。
- 方法的返回值类型应为
void
(无返回值)。
2. 与 InitializingBean
/DisposableBean
的关系
@PostConstruct
的执行早于InitializingBean.afterPropertiesSet()
。@PreDestroy
的执行早于DisposableBean.destroy()
。- 推荐优先使用
@PostConstruct
/@PreDestroy
(标准注解,与框架解耦),而非直接实现接口。
3. 循环依赖的影响
若 Bean 存在循环依赖(如 A 依赖 B,B 依赖 A),@PostConstruct
仍会在依赖注入完成后执行(Spring 通过三级缓存解决循环依赖)。但需注意,此时依赖的 Bean 可能尚未完成自身的 @PostConstruct
方法。
4. 异常处理
@PostConstruct
方法抛出异常会导致 Bean 初始化失败(容器启动时报错)。@PreDestroy
方法抛出异常会被 Spring 捕获并记录,但不会阻止 Bean 销毁(除非异常未被处理)。
5. 性能优化
- 避免在
@PostConstruct
中执行耗时操作(如文件 IO、远程调用),可能导致容器启动缓慢。 @PreDestroy
中的资源清理应尽量轻量(如关闭连接而非重新建立)。
六、总结
@PostConstruct
和 @PreDestroy
是 Spring 中管理 Bean 生命周期的核心注解,分别用于初始化后和销毁前执行自定义逻辑。它们的执行时机严格遵循 Spring 的生命周期流程,适用于资源初始化、清理等场景。理解其源码(如 CommonAnnotationBeanPostProcessor
的处理逻辑)和使用规范(如方法签名限制),有助于开发者编写更健壮、可维护的 Spring 应用。