Spring Bean 生命周期:从出生到销毁的完整旅程
前言
在 Spring 框架中,Bean 是容器管理的核心对象。理解 Bean 的生命周期,不仅有助于写出更健壮的代码,还能在遇到问题时快速定位原因。
你是否遇到过这些问题:
@PostConstruct和afterPropertiesSet谁先执行?- 为什么在构造器里注入的 Bean 还是 null?
BeanPostProcessor到底有什么用?
本文将带你完整走一遍 Spring Bean 从创建到销毁的全过程,并通过代码示例让你彻底搞懂每个阶段。
一、生命周期概览
Spring Bean 的生命周期可以用下面这张图概括:
┌─────────────────────────────────────────────────────────────────────────────┐
│ Spring Bean 生命周期全景图 │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ 容器启动 │
└──────┬───────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 1. 扫描与解析 │
│ └── 扫描 @Component、@Service 等注解,或解析 XML 配置 │
│ └── 创建 BeanDefinition 对象(存储 Bean 的元数据) │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 2. 实例化(Instantiation) │
│ └── 通过反射调用构造器创建对象实例 │
│ └── 💡 此时依赖尚未注入,属性均为默认值 │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 3. 属性填充(Populate Properties) │
│ └── @Autowired 依赖注入 │
│ └── @Value 属性值注入 │
│ └── @Resource、@Inject 等 │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 4. Bean 名称感知(BeanNameAware) │
│ └── setBeanName() - 获取 Bean 在容器中的名称 │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 5. Bean 工厂感知(BeanFactoryAware) │
│ └── setBeanFactory() - 获取 BeanFactory 引用 │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 6. 应用上下文感知(ApplicationContextAware) │
│ └── setApplicationContext() - 获取 ApplicationContext 引用 │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 7. Bean 前置处理器(BeanPostProcessor.beforeInitialization) │
│ └── 对所有 Bean 生效的全局处理 │
│ └── 典型应用:@Autowired 注解解析、代理生成准备 │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 8. 初始化(Initialization) │
│ └── @PostConstruct 标注的方法 │
│ └── InitializingBean.afterPropertiesSet() │
│ └── @Bean(initMethod = "customInit") 自定义初始化方法 │
│ └── 💡 执行顺序:@PostConstruct → afterPropertiesSet → initMethod │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 9. Bean 后置处理器(BeanPostProcessor.postProcessAfterInitialization) │
│ └── 对所有 Bean 生效的全局处理 │
│ └── 典型应用:AOP 代理生成(返回代理对象替换原对象) │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 10. Bean 就绪(Ready to Use) │
│ └── 可被业务代码使用 │
└──────┬───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ 11. 容器关闭(Container Shutdown) │
│ └── @PreDestroy 标注的方法 │
│ └── DisposableBean.destroy() │
│ └── @Bean(destroyMethod = "customDestroy") 自定义销毁方法 │
│ └── 💡 执行顺序:@PreDestroy → destroy → destroyMethod │
└──────────────────────────────────────────────────────────────────────────────┘
二、详细阶段解析
2.1 实例化(Instantiation)
这是 Bean 生命周期的第一步,Spring 通过反射调用构造器创建对象实例。
@Component
public class UserService {
private String name;
// 构造器
public UserService() {
System.out.println("1. 实例化:调用构造器");
System.out.println(" - name = " + name); // 输出 null
}
}
关键点:
- 此时对象刚刚创建,属性都是默认值(null、0、false)
- 依赖注入尚未发生
- 如果 Bean 有多个构造器,Spring 会按规则选择:
@Component
public class OrderService {
private UserService userService;
private ProductService productService;
// 方式1:@Autowired 标注的构造器(推荐)
@Autowired
public OrderService(UserService userService) {
this.userService = userService;
System.out.println("使用 @Autowired 构造器");
}
// 方式2:只有一个构造器时自动使用
public OrderService(UserService userService, ProductService productService) {
this.userService = userService;
this.productService = productService;
System.out.println("唯一构造器自动使用");
}
// 方式3:默认无参构造器
public OrderService() {
System.out.println("使用无参构造器");
}
}
2.2 属性填充(Populate Properties)
实例化之后,Spring 会进行依赖注入。
@Component
public class PaymentService {
// 字段注入
@Autowired
private UserService userService;
// Setter 注入
private OrderService orderService;
@Autowired
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
System.out.println("2. 属性填充:Setter 注入 orderService");
}
// 任意方法注入
private ProductService productService;
@Autowired
public void customInject(ProductService productService) {
this.productService = productService;
System.out.println("2. 属性填充:自定义方法注入 productService");
}
@PostConstruct
public void init() {
System.out.println("此时所有依赖都已注入完成");
System.out.println(" - userService = " + (userService != null ? "已注入" : "null"));
System.out.println(" - orderService = " + (orderService != null ? "已注入" : "null"));
}
}
2.3 Aware 接口感知
Spring 提供了一系列 Aware 接口,让 Bean 感知到容器中的基础设施。
@Component
public class AwareDemoBean implements
BeanNameAware,
BeanFactoryAware,
ApplicationContextAware,
EnvironmentAware,
ResourceLoaderAware,
ApplicationEventPublisherAware {
@Override
public void setBeanName(String name) {
System.out.println("3. BeanNameAware: Bean 名称 = " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("4. BeanFactoryAware: 获取到 BeanFactory");
// 可以在这里获取其他 Bean
// UserService userService = beanFactory.getBean(UserService.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("5. ApplicationContextAware: 获取到 ApplicationContext");
// 可以获取环境、发布事件等
String[] beanNames = applicationContext.getBeanDefinitionNames();
System.out.println(" - 容器中共有 " + beanNames.length + " 个 Bean");
}
@Override
public void setEnvironment(Environment environment) {
System.out.println(" EnvironmentAware: 获取到环境配置");
String property = environment.getProperty("spring.application.name");
System.out.println(" - 应用名称 = " + property);
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
System.out.println(" ResourceLoaderAware: 获取到资源加载器");
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
System.out.println(" ApplicationEventPublisherAware: 获取到事件发布器");
}
}
2.4 BeanPostProcessor(前置处理)
BeanPostProcessor 是 Spring 框架的扩展点之一,允许在所有 Bean 初始化前后插入自定义逻辑。
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("6. BeanPostProcessor.beforeInitialization: " + beanName);
// 示例:为所有 Service 添加日志记录功能
if (bean.getClass().isAnnotationPresent(Service.class)) {
System.out.println(" - 为 " + beanName + " 添加前置处理逻辑");
}
return bean; // 返回原对象或代理对象
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("8. BeanPostProcessor.afterInitialization: " + beanName);
// 示例:为需要监控的方法生成代理
if (bean instanceof MonitoringEnabled) {
System.out.println(" - 为 " + beanName + " 生成监控代理");
return createMonitoringProxy(bean);
}
return bean;
}
private Object createMonitoringProxy(Object target) {
// 生成代理对象的逻辑
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long cost = System.currentTimeMillis() - start;
System.out.println("方法 " + method.getName() + " 耗时: " + cost + "ms");
return result;
}
);
}
}
2.5 初始化(Initialization)
初始化阶段有三种方式可以执行自定义初始化逻辑。
@Component
public class InitDemoBean {
private String data;
// 方式1:@PostConstruct(JSR-250 标准,推荐)
@PostConstruct
public void postConstruct() {
System.out.println("7-A. @PostConstruct 初始化");
// 执行资源初始化、数据加载等
this.data = loadDataFromDB();
System.out.println(" - 数据加载完成: " + data);
}
// 方式2:实现 InitializingBean 接口
// 注意:这种方式与 Spring 框架耦合
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("7-B. InitializingBean.afterPropertiesSet()");
// 校验依赖是否注入完整
if (this.dependency == null) {
throw new IllegalStateException("dependency 不能为空");
}
}
// 方式3:@Bean 的 initMethod 属性(XML 或 Java Config)
public void customInit() {
System.out.println("7-C. @Bean(initMethod) 自定义初始化方法");
// 连接外部系统、启动定时任务等
startScheduledTask();
}
private String loadDataFromDB() {
return "从数据库加载的配置数据";
}
private void startScheduledTask() {
System.out.println(" - 定时任务已启动");
}
}
// 配置类方式
@Configuration
public class AppConfig {
@Bean(initMethod = "customInit")
public InitDemoBean initDemoBean() {
return new InitDemoBean();
}
}
执行顺序:
@PostConstruct(最早执行)
↓
InitializingBean.afterPropertiesSet()
↓
@Bean(initMethod) 自定义初始化方法
2.6 BeanPostProcessor(后置处理)
后置处理是 AOP 实现的关键,Spring 在这里生成代理对象。
@Component
public class AopBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 检查是否需要 AOP 代理
if (bean.getClass().isAnnotationPresent(Transactional.class)) {
System.out.println("为 " + beanName + " 生成事务代理");
return createTransactionProxy(bean);
}
return bean;
}
private Object createTransactionProxy(Object target) {
// 返回代理对象,后续容器中存储的是这个代理对象
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
try {
// 开启事务
beginTransaction();
Object result = method.invoke(target, args);
commitTransaction();
return result;
} catch (Exception e) {
rollbackTransaction();
throw e;
}
}
);
}
}
2.7 销毁阶段
容器关闭时,Bean 会执行销毁逻辑。
@Component
public class DestroyDemoBean {
// 方式1:@PreDestroy(JSR-250 标准,推荐)
@PreDestroy
public void preDestroy() {
System.out.println("10-A. @PreDestroy 销毁前处理");
// 释放资源、关闭连接、停止线程池
closeResources();
}
// 方式2:实现 DisposableBean 接口
@Override
public void destroy() throws Exception {
System.out.println("10-B. DisposableBean.destroy()");
// 清理缓存、刷新数据到磁盘
flushCacheToDisk();
}
// 方式3:@Bean 的 destroyMethod 属性
public void customDestroy() {
System.out.println("10-C. @Bean(destroyMethod) 自定义销毁方法");
// 发送关闭通知、记录日志等
sendShutdownNotification();
}
private void closeResources() {
System.out.println(" - 资源已释放");
}
private void flushCacheToDisk() {
System.out.println(" - 缓存已刷新到磁盘");
}
private void sendShutdownNotification() {
System.out.println(" - 关闭通知已发送");
}
}
三、实战案例
3.1 案例:数据库连接池管理
@Component
public class CustomDataSource implements InitializingBean, DisposableBean {
private HikariDataSource dataSource;
@Value("${db.url}")
private String url;
@Value("${db.username}")
private String username;
@Value("${db.password}")
private String password;
@PostConstruct
public void init() {
System.out.println("初始化配置校验");
if (url == null || username == null) {
throw new IllegalStateException("数据库配置不完整");
}
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("创建并初始化连接池");
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
this.dataSource = new HikariDataSource(config);
System.out.println("连接池初始化完成,最大连接数: " + config.getMaximumPoolSize());
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
@Override
public void destroy() throws Exception {
System.out.println("关闭连接池");
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
System.out.println("连接池已关闭");
}
}
}
3.2 案例:实现 @MyCache 注解
通过 BeanPostProcessor 实现自定义缓存注解。
// 1. 定义缓存注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCache {
String value() default "";
int ttl() default 60; // 缓存过期时间(秒)
}
// 2. 实现 BeanPostProcessor 处理缓存注解
@Component
public class CacheBeanPostProcessor implements BeanPostProcessor {
private final Map<String, Map<Object, Object>> cacheMap = new ConcurrentHashMap<>();
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 扫描所有方法,找到带 @MyCache 注解的方法
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
MyCache cacheAnnotation = method.getAnnotation(MyCache.class);
if (cacheAnnotation != null) {
System.out.println("为方法 " + method.getName() + " 添加缓存功能");
// 生成代理
return createCacheProxy(bean, method, cacheAnnotation);
}
}
return bean;
}
private Object createCacheProxy(Object target, Method method, MyCache cache) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, proxyMethod, args) -> {
if (proxyMethod.equals(method)) {
String cacheKey = cache.value() + ":" + Arrays.toString(args);
// 从缓存获取
Map<Object, Object> methodCache = cacheMap.computeIfAbsent(
method.getName(), k -> new ConcurrentHashMap<>());
if (methodCache.containsKey(cacheKey)) {
System.out.println("缓存命中: " + cacheKey);
return methodCache.get(cacheKey);
}
// 执行原方法
Object result = proxyMethod.invoke(target, args);
// 存入缓存
methodCache.put(cacheKey, result);
System.out.println("缓存未命中,已缓存: " + cacheKey);
// 设置过期(简化版,实际应使用 ScheduledExecutorService)
scheduleCacheEviction(method.getName(), cacheKey, cache.ttl());
return result;
}
return proxyMethod.invoke(target, args);
}
);
}
private void scheduleCacheEviction(String methodName, String key, int ttlSeconds) {
// 定时清除缓存的逻辑
// ...
}
}
// 3. 使用缓存注解
@Service
public class ProductService {
@MyCache(value = "product", ttl = 300)
public Product getProductById(Long id) {
System.out.println("查询数据库获取商品: " + id);
// 模拟慢查询
try { Thread.sleep(100); } catch (InterruptedException e) {}
return new Product(id, "商品" + id, 99.9);
}
}
3.3 案例:启动后预热缓存
@Component
public class CacheWarmer implements ApplicationRunner {
@Autowired
private ProductService productService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("应用启动完成,开始预热缓存...");
// 预热热门商品
List<Long> hotProductIds = getHotProductIds();
ExecutorService executor = Executors.newFixedThreadPool(10);
CountDownLatch latch = new CountDownLatch(hotProductIds.size());
for (Long productId : hotProductIds) {
executor.submit(() -> {
try {
productService.getProductById(productId);
System.out.println("预热商品: " + productId);
} finally {
latch.countDown();
}
});
}
latch.await(30, TimeUnit.SECONDS);
executor.shutdown();
System.out.println("缓存预热完成,共预热 " + hotProductIds.size() + " 个商品");
}
private List<Long> getHotProductIds() {
// 从数据库或统计系统获取热门商品ID
return Arrays.asList(1001L, 1002L, 1003L, 1004L, 1005L);
}
}
四、常见问题与最佳实践
4.1 为什么构造器里 @Autowired 注入为 null?
问题:在构造器中访问注入的依赖,发现为 null。
@Component
public class WrongService {
@Autowired
private UserService userService;
public WrongService() {
// ❌ 错误:此时 userService 还是 null
System.out.println(userService.getUserName()); // NullPointerException!
}
}
原因:构造器执行时,属性填充还未发生。
解决方案:
@Component
public class RightService {
private final UserService userService;
// ✅ 方案1:构造器注入(推荐)
@Autowired
public RightService(UserService userService) {
this.userService = userService;
// 构造器参数已注入,可以安全使用
System.out.println(userService.getUserName());
}
// ✅ 方案2:使用 @PostConstruct
@Autowired
private UserService userService2;
@PostConstruct
public void init() {
// 此时依赖已注入
System.out.println(userService2.getUserName());
}
}
4.2 循环依赖问题
// ❌ 构造器循环依赖:无法解决
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
// 启动报错:BeanCurrentlyInCreationException
// ✅ Setter/字段循环依赖:Spring 可以通过三级缓存解决
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
// 可以正常启动
4.3 生命周期执行顺序验证
@Component
public class LifecycleDemoBean implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, InitializingBean, DisposableBean {
public LifecycleDemoBean() {
System.out.println("1. 构造器");
}
@Autowired
public void setDependency(DependencyBean dependency) {
System.out.println("2. 属性注入: setDependency");
}
@Override
public void setBeanName(String name) {
System.out.println("3. BeanNameAware: " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("4. BeanFactoryAware");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("5. ApplicationContextAware");
}
@PostConstruct
public void postConstruct() {
System.out.println("6. @PostConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("7. InitializingBean.afterPropertiesSet");
}
@Bean(initMethod = "customInit")
public void customInit() {
System.out.println("8. @Bean(initMethod) 自定义初始化");
}
@PreDestroy
public void preDestroy() {
System.out.println("9. @PreDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("10. DisposableBean.destroy");
}
@Bean(destroyMethod = "customDestroy")
public void customDestroy() {
System.out.println("11. @Bean(destroyMethod) 自定义销毁");
}
}
输出顺序:
1. 构造器
2. 属性注入: setDependency
3. BeanNameAware
4. BeanFactoryAware
5. ApplicationContextAware
6. @PostConstruct
7. InitializingBean.afterPropertiesSet
8. @Bean(initMethod) 自定义初始化
9. @PreDestroy
10. DisposableBean.destroy
11. @Bean(destroyMethod) 自定义销毁
4.4 最佳实践总结
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 依赖注入 | 构造器注入 | 不可变、便于测试 |
| 初始化逻辑 | @PostConstruct |
标准、解耦 |
| 销毁逻辑 | @PreDestroy |
标准、解耦 |
| 访问容器 | ApplicationContextAware |
按需使用,避免滥用 |
| 全局 Bean 处理 | BeanPostProcessor |
框架扩展点 |
| 启动后执行 | ApplicationRunner |
专门用于启动后处理 |
五、总结
生命周期关键节点
构造器 → 注入 → Aware → 前置处理 → 初始化 → 后置处理 → 就绪 → 销毁
↑ ↑ ↑ ↑ ↑ ↑ ↑
反射 @Autowired 各种Aware BPP前 @PC/Init BPP后 @PreDestroy
核心要点
- 构造器:最早执行,依赖尚未注入
- 属性注入:发生在构造器之后
- Aware 接口:让 Bean 感知容器基础设施
- BeanPostProcessor:框架扩展的核心,AOP 的基础
- 初始化:
@PostConstruct→afterPropertiesSet→initMethod - 销毁:
@PreDestroy→destroy→destroyMethod
面试高频问题
Q: @PostConstruct 和 afterPropertiesSet 的区别?
@PostConstruct是 JSR-250 标准,与 Spring 解耦afterPropertiesSet是 Spring 特有接口- 执行顺序:
@PostConstruct先执行
Q: BeanPostProcessor 和 InitializingBean 的区别?
InitializingBean针对单个 BeanBeanPostProcessor对所有 Bean 生效
Q: Spring 如何解决循环依赖?
- 构造器循环依赖:无法解决,报错
- Setter/字段循环依赖:通过三级缓存解决
理解 Spring Bean 的生命周期,是掌握 Spring 框架的必经之路。希望这篇文章能帮你建立起清晰的理解,在实际开发中更加得心应手。