Spring Boot 3+ 生命周期管理:用现代方式替代 @PostConstruct 和 @PreDestroy

引言

在 Spring Boot开发中,@PostConstruct@PreDestroy 这两个生命周期注解曾经是管理 Bean 初始化和销毁的常用方式。然而,随着 Java 9+ 的发展,javax.annotation 包被移除,Spring Boot 3+ 也进行了相应调整,建议开发者使用更现代的方式来替代它们。

在这篇文章中,我们将深入探讨:

  • @PostConstruct 的作用与替代方案
  • @PreDestroy 的作用及其替代方式
  • 如何使用 @EventListener(ApplicationReadyEvent.class) 进行初始化
  • 如何使用 DisposableBean@Bean(destroyMethod = "methodName") 进行资源清理

@PostConstruct:Bean 初始化

@PostConstruct 是什么?

@PostConstruct 用于 在 Bean 完成依赖注入后但在正式使用前 运行一个方法,通常用于初始化数据或执行预处理逻辑

问题:没有 @PostConstruct,初始化数据失败

假设我们有一个 UserService,它在应用启动时需要从数据库中加载用户数据到缓存中。如果没有 @PostConstruct,应用启动后,缓存是空的,查询用户时会返回 null

@Service
public class UserService {
    private final UserRepository userRepository;
    private Map<Long, User> userCache = new HashMap<>();

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(Long id) {
        return userCache.get(id); // 查询时,缓存为空
    }
}

问题userCache 在应用启动时为空,导致查询不到数据。

解决方案:使用 @PostConstruct

为了在应用启动时自动加载用户数据,我们可以使用 @PostConstruct

import jakarta.annotation
.PostConstruct;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;

@Service
public class UserService {
    private final UserRepository userRepository;
    private Map<Long, User> userCache = new HashMap<>();

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @PostConstruct
    public void init() {
        System.out.println("初始化用户缓存...");
        userRepository.findAll().forEach(user -> userCache.put(user.getId(), user));
        System.out.println("用户数据加载完成!");
    }

    public User getUserById(Long id) {
        return userCache.get(id); // 现在缓存已预加载,可以正常查询
    }
}

工作原理

  1. UserService Bean 被创建,Spring 自动注入 UserRepository
  2. @PostConstruct 标注的方法 init() 在 Bean 初始化后自动执行。
  3. init() 方法从数据库加载用户数据,并存入 userCache

Spring Boot 3+ 替代方案:@EventListener(ApplicationReadyEvent.class)

虽然 @PostConstruct 仍可用(被迁移到 jakarta.annotation),但 Spring Boot 3+ 推荐使用 @EventListener(ApplicationReadyEvent.class),它在 整个应用启动完成 后运行,适用于更复杂的初始化任务。

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class StartupInitializer {

    @EventListener(ApplicationReadyEvent.class)
    public void init() {
        System.out.println("应用启动完成,执行初始化逻辑...");
        loadData();
    }

    private void loadData() {
        System.out.println("加载初始数据...");
        // 这里可以执行数据库加载、缓存初始化等任务
    }
}

@EventListener(ApplicationReadyEvent.class) 的优点

  • 只有在 整个 Spring 容器完全启动 后才执行,保证所有 Bean 都已准备好。
  • 适用于需要数据库、缓存等外部组件支持的初始化操作。
  • 避免了 @PostConstruct 可能遇到的部分依赖未就绪的问题。

@PreDestroy:Bean 销毁

@PreDestroy 是什么?

@PreDestroy 用于 在 Bean 被销毁前执行清理操作,例如关闭文件、释放数据库连接、终止后台任务等。

示例:文件服务

假设我们有一个 FileService,它在启动时打开一个文件,在应用关闭时需要清理资源:

import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;

@Service
public class FileService {

    private String fileResource = "文件已打开";

    @PreDestroy
    public void cleanUp() {
        System.out.println("应用关闭,释放资源...");
        fileResource = null; // 模拟关闭文件
    }
}

作用

  1. 应用启动后,FileService 被创建,打开文件资源。
  2. 应用关闭时,Spring 调用 @PreDestroy 方法,确保资源释放。

Spring Boot 3+ 替代方案

方式 1:实现 DisposableBean

Spring 提供了 DisposableBean 接口,我们可以通过 destroy() 方法处理资源清理:

import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service;

@Service
public class MyService implements DisposableBean {

    @Override
    public void destroy() throws Exception {
        System.out.println("清理资源...");
    }
}

destroy() 方法在 Bean 销毁前自动执行,效果等同于 @PreDestroy

方式 2:使用 @Bean(destroyMethod = "方法名")

如果我们是在 @Configuration 类中定义 Bean,可以指定 destroyMethod

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean(destroyMethod = "close")
    public MyService myService() {
        return new MyService();
    }
}

class MyService {

    public void close() {
        System.out.println("应用关闭,执行清理...");
    }
}
展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java