在 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); // 现在缓存已预加载,可以正常查询
}
}
工作原理
UserService
Bean 被创建,Spring 自动注入 UserRepository
。@PostConstruct
标注的方法 init()
在 Bean 初始化后自动执行。init()
方法从数据库加载用户数据,并存入 userCache
。@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)
的优点
@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; // 模拟关闭文件
}
}
作用
FileService
被创建,打开文件资源。@PreDestroy
方法,确保资源释放。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
。
@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删除。