人人都会AI编程

4.2 初始化与销毁的多种实现方式与执行顺序

更新时间:2026-07-10

Spring 容器不仅负责 Bean 的创建和依赖注入,还提供了多个扩展点,让开发者在 Bean 就绪时执行初始化逻辑,在容器关闭前完成资源清理。这些扩展点既有基于注解的、基于接口的,也有在配置中声明的方式。本小节将它们逐一展开,并厘清它们在同一个 Bean 中的执行顺序。

4.2.1 方式一:JSR-250 注解 —— @PostConstruct 与 @PreDestroy

这是最通用、最推荐的方式。@PostConstruct 标注的方法会在依赖注入完成后自动执行,用于执行初始化逻辑;@PreDestroy 标注的方法会在 Bean 被销毁前执行,用于释放资源。

@Component
public class DataCollector {

    private List<String> buffer = new ArrayList<>();

    @PostConstruct
    public void init() {
        System.out.println("DataCollector 初始化:预热缓存连接...");
        // 加载基础配置、建立网络连接等
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("DataCollector 销毁:刷新缓冲区、关闭连接...");
        buffer.clear();
    }
}

特点

  • 方法可以任意命名,访问权限推荐 public,但不能是 static
  • 要求开启注解支持(Spring Boot 自动开启,传统 Spring 需 <context:annotation-config/>@Configuration 扫描)。
  • 简单、直观,与 Spring 框架解耦(属于 Java 标准注解),可移植性好。

4.2.2 方式二:Spring 接口 —— InitializingBean 和 DisposableBean

通过实现 org.springframework.beans.factory.InitializingBean 接口的 afterPropertiesSet() 方法,或 DisposableBean 接口的 destroy() 方法,也能达成同样的目的。

@Component
public class ReportGenerator implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("ReportGenerator 初始化:加载报表模板...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("ReportGenerator 销毁:清理临时文件...");
    }
}

特点

  • 比注解更“重”,因为代码直接耦合了 Spring 的特定接口。
  • 在一些旧项目中常见,现在官方也更推荐注解或配置方式。
  • 方法签名固定,编译器会强制检查,但灵活性不如注解。

4.2.3 方式三:@Bean 注解的 initMethod 和 destroyMethod

@Configuration 类中使用 @Bean 声明 Bean 时,可以通过 initMethoddestroyMethod 指定自定义的初始化和销毁方法。这种方式特别适合管理第三方类库的 Bean,因为无法修改第三方源码加注解。

public class ExternalCacheManager {
    public void start() {
        System.out.println("ExternalCacheManager 启动");
    }
    public void stop() {
        System.out.println("ExternalCacheManager 停止");
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "start", destroyMethod = "stop")
    public ExternalCacheManager cacheManager() {
        return new ExternalCacheManager();
    }
}

特点

  • 完全非侵入,第三方类无需实现任何接口或添加注解。
  • destroyMethod 有默认推断机制:当用 @Bean 声明时,Spring 会自动寻找名为 closeshutdown 的方法(可设置 destroyMethod = "" 禁用)。
  • 可以实现基于配置的灵活控制,比如在不同环境下指定不同的初始方法。

4.2.4 方式四:XML 配置中的 init-method 和 destroy-method

如果项目仍在使用 XML 配置,可以在 <bean> 标签上指定初始化和销毁方法:

<bean id="externalCache" class="com.example.ExternalCacheManager"
      init-method="start" destroy-method="stop" />

这与 @Bean 的属性名一致,用途和场景完全相同,都是为普通 Java 对象赋予生命周期回调。

4.2.5 方式五:@Component 与 XML 混合(基本废弃)

早期的 Spring 允许在 @Component 注解中指定 initMethod 属性,但 Spring 5.x 之后已不建议使用,此处仅提及以避免疑惑。

4.2.6 多种方式的执行顺序

当一个 Bean 同时采用多种方式声明初始化或销毁逻辑时,Spring 按固定顺序依次调用它们。了解这个顺序对排查问题至关重要——尤其是当不同回调中依赖了彼此的状态。

初始化执行顺序

  1. @PostConstruct 注解的方法。
  2. InitializingBean 接口的 afterPropertiesSet()
  3. @Bean(initMethod) 或 XML init-method 指定的自定义方法。

实例验证:假设有一个 Bean 同时使用了三种方式:

@Component
public class LifecycleBean implements InitializingBean, DisposableBean {

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

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

    public void customInit() {
        System.out.println("3. customInit (来自 @Bean initMethod)");
    }

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

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

    public void customDestroy() {
        System.out.println("3. customDestroy");
    }
}

配合配置类:

@Bean(initMethod = "customInit", destroyMethod = "customDestroy")
public LifecycleBean lifecycleBean() {
    return new LifecycleBean();
}

启动和关闭容器,控制台输出将严格遵循上述顺序。掌握这个顺序,实际开发中就可以自由组合不同方式而不会互相干扰。

4.2.7 真实场景中的选择建议

  • 业务 Bean 的初始化:优先使用 @PostConstruct,代码最清晰且与框架解耦。
  • 第三方类管理:采用 @BeaninitMethod / destroyMethod,不修改第三方源码。
  • 遗留代码维护:若已有实现 InitializingBean 的 Bean,了解执行顺序即可,不必强制重写。
  • 避免混合使用:单一 Bean 中同时使用多种方式会增加理解成本,除非必要(比如既有注解又有 XML 遗留配置),否则只在一种方式上实现生命周期逻辑。
  • 注意销毁回调触发条件@PreDestroy 等只在容器正常关闭(close())时触发,如果 JVM 直接崩溃或 kill -9,这些回调不会被调用。关键资源(如文件锁)仍需额外保障。

4.2.8 补充:BeanPostProcessor 层面的初始化钩子

除了 Bean 自身提供的回调,Spring 还允许通过 BeanPostProcessorpostProcessBeforeInitializationpostProcessAfterInitialization 方法,在 Bean 初始化前后插入自定义逻辑。这些处理器作用于所有 Bean,常用于代理生成、属性校验等框架级行为。它们的执行时机在 @PostConstructafterPropertiesSet 的前后,属于容器级别而非单个 Bean 的回调。

现在,你已经掌握了 Bean 初始化与销毁的全部可用手段及其协同规则。在下一个部分,我们将考察 Bean 的作用域如何影响这些生命周期回调的调用时机。