Spring Bean 生命周期详解:初始化与销毁方式对比与实践
目录
- 一、Spring Bean 生命周期解析
- 1、Bean 初始化方式
- 1.1 @PostConstruct
- 1.2 InitializingBean
- 1.3 init-method
- 2、Bean 销毁方式
- 2.1 @PreDestroy
- 2.2 DisposableBean
- 2.3 destroy-method
- 3、执行顺序对比
- 4、实战案例(完整生命周期)
- 5、总结
- 二、现实开发中的使用场景
- 1、初始化三兄弟
- 2、销毁三兄弟
- 三、总结对比(开发实践)
一、Spring Bean 生命周期解析
在 Spring 应用中,Bean 的生命周期主要分为两个关键阶段:
- 初始化(Initialization)
- 销毁(Destruction)
Spring 提供了多种方式来自定义这两个阶段的逻辑,主要包括:
- 初始化:
@PostConstruct
、InitializingBean
、init-method
- 销毁:
@PreDestroy
、DisposableBean
、destroy-method
1、Bean 初始化方式
1.1 @PostConstruct
- JSR-250 标准注解
- 方法要求:
public void
、无参 - 推荐方式,最简洁
@Service
public class OrderService {@PostConstructpublic void init() {System.out.println("OrderService 初始化逻辑执行...");}
}
1.2 InitializingBean
- 实现 Spring 提供的
InitializingBean
接口 - 必须实现
afterPropertiesSet()
方法 - 强耦合:类依赖 Spring
@Component
public class ProductService implements InitializingBean {@Overridepublic void afterPropertiesSet() {System.out.println("ProductService 初始化逻辑执行...");}
}
1.3 init-method
- 在
@Bean
或 XML 中配置 - 方法名随意,但必须配置
- 解耦:类本身不依赖 Spring
public class UserService {public void customInit() {System.out.println("UserService 初始化逻辑执行...");}
}@Configuration
public class AppConfig {@Bean(initMethod = "customInit")public UserService userService() {return new UserService();}
}
2、Bean 销毁方式
2.1 @PreDestroy
- JSR-250 标准注解
- 方法必须是
void
、无参 - 常与
@PostConstruct
配合使用
@Service
public class OrderService {@PreDestroypublic void destroy() {System.out.println("OrderService 销毁逻辑执行...");}
}
2.2 DisposableBean
- 实现 Spring 提供的
DisposableBean
接口 - 必须实现
destroy()
方法 - 强耦合,但语义清晰
@Component
public class ProductService implements DisposableBean {@Overridepublic void destroy() {System.out.println("ProductService 销毁逻辑执行...");}
}
2.3 destroy-method
- 在
@Bean
或 XML 中配置 - 方法名随意,但必须配置
public class UserService {public void customDestroy() {System.out.println("UserService 销毁逻辑执行...");}
}@Configuration
public class AppConfig {@Bean(destroyMethod = "customDestroy")public UserService userService() {return new UserService();}
}
3、执行顺序对比
假设一个 Bean 同时实现了三种方式,执行顺序如下:
初始化顺序:
@PostConstruct
InitializingBean.afterPropertiesSet()
init-method
销毁顺序:
@PreDestroy
DisposableBean.destroy()
destroy-method
4、实战案例(完整生命周期)
public class DemoService implements InitializingBean, DisposableBean {@PostConstructpublic void postConstructInit() {System.out.println("@PostConstruct 初始化执行...");}@Overridepublic void afterPropertiesSet() {System.out.println("InitializingBean.afterPropertiesSet() 初始化执行...");}public void customInit() {System.out.println("init-method 初始化执行...");}@PreDestroypublic void preDestroy() {System.out.println("@PreDestroy 销毁执行...");}@Overridepublic void destroy() {System.out.println("DisposableBean.destroy() 销毁执行...");}public void customDestroy() {System.out.println("destroy-method 销毁执行...");}
}@Configuration
class DemoConfig {@Bean(initMethod = "customInit", destroyMethod = "customDestroy")public DemoService demoService() {return new DemoService();}
}
启动和关闭应用的输出结果:
@PostConstruct 初始化执行...
InitializingBean.afterPropertiesSet() 初始化执行...
init-method 初始化执行...@PreDestroy 销毁执行...
DisposableBean.destroy() 销毁执行...
destroy-method 销毁执行...
5、总结
初始化
- 推荐
@PostConstruct
:最常用、最简洁 init-method
:解耦、灵活,适合第三方库或不想耦合 Spring 的场景InitializingBean
:适合需要明确表达“依赖 Spring 生命周期”的场景
销毁
- 推荐
@PreDestroy
:最直观、常用 destroy-method
:解耦、灵活DisposableBean
:语义明确,适合对生命周期严格要求的场景
简要说明
- 初始化三兄弟:
@PostConstruct
→InitializingBean
→init-method
- 销毁三兄弟:
@PreDestroy
→DisposableBean
→destroy-method
- 推荐:常用场景下
@PostConstruct + @PreDestroy
就足够,特殊情况再用其他方式。
二、现实开发中的使用场景
1、初始化三兄弟
- @PostConstruct —— 日常开发首选
典型场景:
- 初始化缓存数据
- 启动时加载配置
- 注册监听器或定时任务
- 建立外部服务连接(如 Redis、消息队列、ElasticSearch 客户端)
案例:
@Service
public class CacheService {private final RedisTemplate<String, Object> redisTemplate;public CacheService(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}@PostConstructpublic void loadCache() {System.out.println("系统启动 -> 预热缓存数据...");// redisTemplate.opsForValue().set("key", "value");}
}
👉 大多数业务 Bean 的初始化逻辑都推荐用 @PostConstruct
,简洁直观。
- InitializingBean —— 需要强烈依赖 Spring 生命周期语义时
典型场景:
- 框架/中间件开发中,需要明确表明这个 Bean 必须依赖 Spring 生命周期
- 类似组件内核逻辑,初始化必须在依赖注入完成后严格执行
案例:
@Component
public class MQConsumer implements InitializingBean {@Overridepublic void afterPropertiesSet() {System.out.println("依赖注入完成后 -> 启动 MQ 消费者线程...");}
}
👉 更多出现在 基础设施层 或 Spring Starter 开发里。普通业务开发很少用。
- init-method —— 解耦第三方类
典型场景:
- 第三方类无法改源码
- 又想在 Bean 创建时执行特定初始化方法
- 保证代码和 Spring 解耦
案例:
public class ExternalClient {public void connect() {System.out.println("连接外部系统...");}
}@Configuration
public class ClientConfig {@Bean(initMethod = "connect")public ExternalClient externalClient() {return new ExternalClient();}
}
👉 在实际开发中,init-method
特别适合 引入第三方 SDK 或 老代码改造场景。
2、销毁三兄弟
- @PreDestroy —— 业务代码的首选
典型场景:
- 释放资源(关闭线程池、关闭连接池)
- 清理缓存数据
- 注销注册中心服务
案例:
@Service
public class WorkerPoolService {private ExecutorService executor = Executors.newFixedThreadPool(4);@PreDestroypublic void shutdown() {System.out.println("应用关闭 -> 释放线程池资源");executor.shutdown();}
}
👉 大多数业务场景首选 @PreDestroy
,语义清晰。
- DisposableBean —— 框架层使用
典型场景:
- 和
InitializingBean
一样,强调生命周期语义 - 适合做 框架封装 或 Spring Starter,便于 Spring 统一管理
案例:
@Component
public class MetricsCollector implements DisposableBean {@Overridepublic void destroy() {System.out.println("关闭应用 -> 上传剩余指标数据");}
}
👉 多见于框架底层,普通业务 Bean 很少直接用。
- destroy-method —— 第三方类资源释放
典型场景:
- 依然是第三方类或旧代码改造
- 无法修改源码,但要在 Spring 容器销毁时执行清理逻辑
案例:
public class ExternalClient {public void close() {System.out.println("断开外部系统连接...");}
}@Configuration
public class ClientConfig {@Bean(destroyMethod = "close")public ExternalClient externalClient() {return new ExternalClient();}
}
👉 在引入外部库(比如数据库驱动、消息客户端)的场景中很实用。
三、总结对比(开发实践)
阶段 | 推荐首选 | 使用场景 | 替补方案 | 使用场景 |
---|---|---|---|---|
初始化 | @PostConstruct | 绝大多数业务逻辑初始化 | init-method | 第三方类、解耦 Spring |
InitializingBean | 框架/中间件,强调生命周期语义 | - | - | |
销毁 | @PreDestroy | 日常业务资源释放 | destroy-method | 第三方类、老代码 |
DisposableBean | 框架封装、Starter 开发 | - | - |
👉 现实开发建议:
- 普通业务类 → 用
@PostConstruct
+@PreDestroy
- 第三方类/旧代码 → 用
init-method
+destroy-method
- 框架层/Starter 开发 → 用
InitializingBean
+DisposableBean