@PostConstruct 与@PreDestroy 详解及详细源码展示

@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 的依赖注入完成后、初始化完成前执行(即“后构造”阶段)。
  • 方法可以是 publicprotectedprivate,但不能是 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 就绪

具体流程:

  1. Spring 通过反射创建 Bean 实例(构造器调用)。
  2. 注入依赖(如 @Autowired 字段、方法参数)。
  3. 调用 @PostConstruct 标记的方法(此时 Bean 已完成依赖注入,但未执行其他初始化逻辑)。

2. @PreDestroy 的执行时机

Bean 销毁阶段的顺序:
DisposableBean.destroy()@PreDestroy 方法 → Bean 销毁(从容器中移除)

具体流程:

  1. 容器触发 Bean 销毁(如应用关闭、手动调用 destroy())。
  2. 调用 @PreDestroy 标记的方法(此时 Bean 仍可使用,但即将被销毁)。
  3. 若 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 通过以下顺序确保生命周期方法的正确调用:

  1. 构造器调用 → 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 应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值