单例 Bean 的完整生命周期及初始化/销毁方法实现
一、单例 Bean 的完整生命周期阶段
生命周期流程图
```
容器启动
↓
1. 实例化(构造方法)
↓
2. 属性注入(setter/字段注入)
↓
3. Aware接口回调(BeanNameAware等)
↓
4. BeanPostProcessor前置处理
↓
5. @PostConstruct方法执行
↓
6. InitializingBean.afterPropertiesSet()
↓
7. 自定义init-method
↓
8. BeanPostProcessor后置处理
↓
9. Bean就绪可用
↓
容器关闭
↓
10. @PreDestroy方法执行
↓
11. DisposableBean.destroy()
↓
12. 自定义destroy-method
```
二、初始化方法实现的 5 种方式
方式1:@PostConstruct 注解(最常用、推荐)
```java
@Component
public class MyService {
@PostConstruct // 项目启动时自动执行
public void init() {
System.out.println("1. @PostConstruct 初始化方法执行");
System.out.println("时机:在所有依赖注入完成后,InitializingBean之前");
// 初始化操作:加载配置、预热缓存、建立连接等
loadConfiguration();
warmUpCache();
initDatabaseConnection();
}
private void loadConfiguration() {
// 加载配置
}
private void warmUpCache() {
// 预热缓存
}
private void initDatabaseConnection() {
// 初始化数据库连接
}
}
```
方式2:实现 InitializingBean 接口
```java
@Component
public class CacheService implements InitializingBean {
private Map<String, Object> cache;
@Override // 项目启动时自动执行
public void afterPropertiesSet() throws Exception {
System.out.println("2. InitializingBean.afterPropertiesSet() 执行");
System.out.println("时机:在@PostConstruct之后,init-method之前");
// 初始化缓存
cache = new ConcurrentHashMap<>();
preloadCache();
}
private void preloadCache() {
// 预加载热点数据
cache.put("config", loadConfigFromDB());
cache.put("hotProducts", loadHotProducts());
}
}
```
方式3:@Bean 注解的 initMethod 属性
```java
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public ExternalService externalService() {
return new ExternalService();
}
}
// 普通类,不需要@Component
public class ExternalService {
public void init() { // 方法名任意
System.out.println("3. 自定义init-method执行");
System.out.println("时机:在InitializingBean之后");
// 外部服务初始化
connectToExternalAPI();
validateCredentials();
}
public void cleanup() {
System.out.println("清理外部连接");
}
}
```
方式4:SmartInitializingSingleton(所有单例Bean初始化完成后)
```java
@Component
public class GlobalInitializer implements SmartInitializingSingleton {
@Autowired
private List<SomeService> services; // 可以注入所有相关Bean
@Override
// 当所有单例Bean都初始化完成后执行
public void afterSingletonsInstantiated() {
System.out.println("4. SmartInitializingSingleton执行");
System.out.println("时机:所有单例Bean都完成初始化后");
System.out.println("当前已初始化Bean数量: " + services.size());
// 全局初始化操作
initializeCrossServiceDependencies();
startBackgroundJobs();
}
private void initializeCrossServiceDependencies() {
// 解决跨服务依赖
}
}
```
方式5:CommandLineRunner / ApplicationRunner(应用启动后)
```java
@Component
@Order(1) // 控制执行顺序,数字越小优先级越高
public class StartupRunner implements CommandLineRunner {
@Override
// 在Spring Boot应用完全启动后执行
public void run(String... args) throws Exception {
System.out.println("5. CommandLineRunner执行");
System.out.println("时机:Spring Boot应用完全启动后");
// 应用启动后需要执行的操作
checkExternalServices();
sendStartupNotification();
scheduleRegularTasks();
}
}
// ApplicationRunner 使用键值对参数
@Component
@Order(2)
public class AnotherRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws ApplicationRunner {
System.out.println("ApplicationRunner执行");
System.out.println("启动参数: " + args.getOptionNames());
}
}
```
三、销毁方法实现的 3 种方式
方式1:@PreDestroy 注解(最常用、推荐)
```java
@Component
public class ResourceHolder {
private List<Connection> connections = new ArrayList<>();
private ScheduledExecutorService executor;
@PostConstruct
public void init() {
executor = Executors.newScheduledThreadPool(2);
System.out.println("线程池初始化完成");
}
@PreDestroy // 应用关闭时自动执行
public void cleanup() {
System.out.println("1. @PreDestroy 清理方法执行");
// 1. 关闭线程池
if (executor != null) {
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
// 2. 关闭数据库连接
connections.forEach(this::closeConnection);
// 3. 清理缓存
clearCaches();
System.out.println("所有资源已清理完成");
}
}
```
方式2:实现 DisposableBean 接口
```java
@Component
public class DatabasePool implements DisposableBean {
private DataSource dataSource;
@Override // 应用关闭时自动执行
public void destroy() throws Exception {
System.out.println("2. DisposableBean.destroy() 执行");
if (dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).close();
System.out.println("数据库连接池已关闭");
}
}
}
```
方式3:@Bean 注解的 destroyMethod 属性
```java
@Configuration
public class DataSourceConfig {
@Bean(destroyMethod = "shutdown")
public ExecutorService executorService() {
return Executors.newFixedThreadPool(10);
}
@Bean(destroyMethod = "close") // 对于有close()方法的Bean,Spring会自动检测
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
// 配置数据源
return new HikariDataSource(config);
}
}
```
四、完整示例:资源管理 Bean
```java
@Component
@Slf4j
public class ResourceManager implements
InitializingBean, DisposableBean, ApplicationListener<ContextClosedEvent> {
// === 资源定义 ===
private ScheduledExecutorService scheduler;
private Connection externalApiConnection;
private Map<String, Object> localCache;
private WebSocketClient webSocketClient;
// === 1. 构造方法 ===
public ResourceManager() {
log.info("1. 构造方法执行 - 创建ResourceManager实例");
}
// === 2. 属性注入 ===
@Value("${app.api.timeout:5000}")
private int apiTimeout;
@Autowired
private Environment env;
// === 3. @PostConstruct ===
@PostConstruct
public void postConstructInit() {
log.info("3. @PostConstruct - 第一阶段初始化");
// 初始化本地缓存
localCache = new ConcurrentHashMap<>();
localCache.put("startupTime", System.currentTimeMillis());
// 创建线程池(但不启动)
scheduler = Executors.newScheduledThreadPool(3,
new ThreadFactoryBuilder()
.setNameFormat("resource-mgr-%d")
.setDaemon(true)
.build());
}
// === 4. InitializingBean ===
@Override
public void afterPropertiesSet() throws Exception {
log.info("4. InitializingBean.afterPropertiesSet() - 第二阶段初始化");
// 连接外部API
externalApiConnection = connectToExternalAPI();
// 启动WebSocket客户端
if (env.acceptsProfiles(Profiles.of("prod", "staging"))) {
webSocketClient = startWebSocketClient();
}
// 启动定时任务
startScheduledTasks();
}
// === 5. 自定义初始化方法 ===
public void customInit() {
log.info("5. 自定义初始化方法 - 第三阶段初始化");
// 预热缓存
preloadCache();
// 发送启动通知
sendStartupNotification();
}
// === 6. 业务方法 ===
public Object getResource(String key) {
return localCache.get(key);
}
// === 7. @PreDestroy ===
@PreDestroy
public void preDestroyCleanup() {
log.info("7. @PreDestroy - 第一阶段清理");
// 清理缓存
localCache.clear();
log.info("本地缓存已清理");
}
// === 8. DisposableBean ===
@Override
public void destroy() throws Exception {
log.info("8. DisposableBean.destroy() - 第二阶段清理");
// 关闭外部连接
if (externalApiConnection != null) {
externalApiConnection.close();
log.info("外部API连接已关闭");
}
// 关闭WebSocket
if (webSocketClient != null) {
webSocketClient.close();
log.info("WebSocket连接已关闭");
}
}
// === 9. 自定义销毁方法 ===
public void customDestroy() {
log.info("9. 自定义销毁方法 - 第三阶段清理");
// 关闭线程池
if (scheduler != null) {
scheduler.shutdownNow();
log.info("线程池已关闭");
}
}
// === 10. 监听容器关闭事件 ===
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("10. 收到ContextClosedEvent - 容器开始关闭");
// 确保所有清理工作完成
waitForCleanupCompletion();
log.info("所有清理工作已完成");
}
// === 辅助方法 ===
private Connection connectToExternalAPI() {
log.info("连接到外部API,超时设置: {}ms", apiTimeout);
return null; // 实际返回连接对象
}
private WebSocketClient startWebSocketClient() {
log.info("启动WebSocket客户端");
return null;
}
private void startScheduledTasks() {
scheduler.scheduleAtFixedRate(() -> {
refreshCache();
}, 0, 5, TimeUnit.MINUTES);
log.info("定时任务已启动");
}
private void preloadCache() {
// 预加载数据
localCache.put("config", loadConfig());
localCache.put("users", loadActiveUsers());
log.info("缓存预热完成,已加载{}项数据", localCache.size());
}
}
```
五、Spring Boot 特定的启动方式
1. ApplicationReadyEvent(应用完全就绪后)
```java
@Component
public class ApplicationReadyListener {
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
System.out.println("应用完全就绪,可以开始处理请求");
System.out.println("时机:在所有Bean初始化、CommandLineRunner执行之后");
// 此时可以:
// 1. 发送应用启动成功通知
// 2. 注册到服务注册中心
// 3. 开始接收外部请求
}
}
```
2. SpringApplication 的 ApplicationRunner
```java
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
// 添加初始化器
app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("ApplicationContext初始化前执行");
}
});
// 添加监听器
app.addListeners(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartedEvent) {
System.out.println("应用启动事件");
}
}
});
app.run(args);
}
}
```
六、实际应用场景示例
场景1:缓存预热服务
```java
@Component
@Slf4j
public class CacheWarmupService {
@Autowired
private ProductRepository productRepository;
@Autowired
private CacheManager cacheManager;
@Autowired
private MetricRegistry metrics;
@PostConstruct
@Order(Ordered.LOWEST_PRECEDENCE) // 最后执行
public void warmUpAllCaches() {
log.info("开始全局缓存预热...");
long startTime = System.currentTimeMillis();
// 1. 预热商品缓存
warmUpProductCache();
// 2. 预热用户缓存
warmUpUserCache();
// 3. 预热配置缓存
warmUpConfigCache();
long duration = System.currentTimeMillis() - startTime;
log.info("缓存预热完成,耗时: {}ms", duration);
// 记录指标
metrics.timer("cache.warmup.time").update(duration, TimeUnit.MILLISECONDS);
}
@PreDestroy
public void clearAllCaches() {
log.info("应用关闭,清理所有缓存...");
// 清理所有缓存
cacheManager.getCacheNames().forEach(cacheName -> {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
log.debug("清理缓存: {}", cacheName);
}
});
log.info("所有缓存已清理");
}
}
```
场景2:WebSocket 连接管理
```java
@Component
public class WebSocketManager implements SmartLifecycle {
private WebSocketSession session;
private volatile boolean running = false;
@PostConstruct
public void init() {
log.info("WebSocket管理器初始化");
}
@Override
public void start() {
if (!running) {
log.info("启动WebSocket连接...");
try {
session = connectToServer();
running = true;
log.info("WebSocket连接建立成功");
} catch (Exception e) {
log.error("WebSocket连接失败", e);
}
}
}
@Override
public void stop() {
if (running) {
log.info("关闭WebSocket连接...");
try {
if (session != null && session.isOpen()) {
session.close();
}
} catch (Exception e) {
log.error("关闭WebSocket连接时出错", e);
} finally {
running = false;
session = null;
}
}
}
@PreDestroy
public void cleanup() {
stop();
log.info("WebSocket管理器清理完成");
}
@Override
public boolean isRunning() {
return running;
}
}
```
场景3:分布式锁初始化
```java
@Component
public class DistributedLockInitializer implements ApplicationRunner {
@Autowired
private RedissonClient redissonClient;
@Value("${app.lock.keys}")
private List<String> lockKeys;
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("初始化分布式锁...");
// 预创建所有需要的锁,避免运行时创建的开销
lockKeys.forEach(key -> {
RLock lock = redissonClient.getLock(key);
lock.tryLock(0, TimeUnit.SECONDS); // 立即释放,只是为了初始化
lock.unlock();
});
log.info("已初始化 {} 个分布式锁", lockKeys.size());
}
}
```
七、原理深入解析
1. 初始化执行顺序的原理
```java
// Spring 内部调用顺序(简化版)
public abstract class AbstractAutowireCapableBeanFactory {
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
// 1. 执行 Aware 接口方法
invokeAwareMethods(beanName, bean);
// 2. BeanPostProcessor 前置处理
wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
try {
// 3. 执行初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(...);
}
// 4. BeanPostProcessor 后置处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = bean instanceof InitializingBean;
// 3.1 先执行 @PostConstruct 方法(通过 CommonAnnotationBeanPostProcessor)
// 3.2 再执行 InitializingBean.afterPropertiesSet()
if (isInitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// 3.3 最后执行自定义的 init-method
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName)) {
invokeCustomInitMethod(beanName, bean, initMethodName);
}
}
}
}
```
2. 销毁执行顺序的原理
```java
public class DisposableBeanAdapter implements DisposableBean {
@Override
public void destroy() throws Exception {
// 1. 先执行 @PreDestroy 方法
if (this.preDestroyMethod != null) {
ReflectionUtils.invokeMethod(this.preDestroyMethod, this.bean);
}
// 2. 再执行 DisposableBean.destroy()
if (this.disposableBean != null) {
this.disposableBean.destroy();
}
// 3. 最后执行自定义的 destroy-method
if (this.destroyMethod != null) {
ReflectionUtils.invokeMethod(this.destroyMethod, this.bean);
}
}
}
```
3. 生命周期管理的底层实现
```java
// Spring 通过 BeanPostProcessor 管理生命周期
@Component
public class CommonAnnotationBeanPostProcessor implements BeanPostProcessor {
// 处理 @PostConstruct
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 查找并执行 @PostConstruct 方法
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
metadata.invokeInitMethods(bean, beanName);
return bean;
}
// 处理 @PreDestroy
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 注册销毁回调
if (requiresDestruction(bean, beanName)) {
registerDestructionCallback(beanName, bean);
}
return bean;
}
}
```
八、最佳实践总结
选择指南
场景 推荐方式 理由
普通的初始化逻辑 @PostConstruct 简单、直观、解耦
需要实现接口的Bean InitializingBean 与框架集成更好
第三方库的Bean @Bean(initMethod) 不修改源码
所有Bean初始化完成后 SmartInitializingSingleton 处理跨Bean依赖
应用完全启动后 ApplicationRunner 确保所有服务就绪
资源清理 @PreDestroy 简单可靠
复杂资源管理 DisposableBean + @PreDestroy 分级清理
注意事项
1. 执行顺序问题:多个初始化方法会按固定顺序执行
2. 异常处理:初始化失败会导致Bean创建失败
3. 循环依赖:避免在初始化方法中引用可能产生循环依赖的Bean
4. 性能影响:初始化方法中的耗时操作会影响启动时间
5. 事务问题:初始化方法中的数据库操作不受事务管理
调试技巧
```java
// 查看Bean的初始化状态
@Component
public class LifecycleDebugger implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("准备初始化: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("初始化完成: " + beanName);
return bean;
}
}
// 使用 actuator 端点查看Bean信息
// application.yml
management:
endpoints:
web:
exposure:
include: beans
// 访问: http://localhost:8080/actuator/beans
```
通过合理使用这些生命周期方法,可以确保应用的优雅启动和安全关闭,有效管理资源。