Spring的生命周期

Posted by     "zengchengjie" on Wednesday, June 17, 2020

Spring Bean 生命周期:从出生到销毁的完整旅程

前言

在 Spring 框架中,Bean 是容器管理的核心对象。理解 Bean 的生命周期,不仅有助于写出更健壮的代码,还能在遇到问题时快速定位原因。

你是否遇到过这些问题:

  • @PostConstructafterPropertiesSet 谁先执行?
  • 为什么在构造器里注入的 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

核心要点

  1. 构造器:最早执行,依赖尚未注入
  2. 属性注入:发生在构造器之后
  3. Aware 接口:让 Bean 感知容器基础设施
  4. BeanPostProcessor:框架扩展的核心,AOP 的基础
  5. 初始化@PostConstructafterPropertiesSetinitMethod
  6. 销毁@PreDestroydestroydestroyMethod

面试高频问题

Q: @PostConstructafterPropertiesSet 的区别?

  • @PostConstruct 是 JSR-250 标准,与 Spring 解耦
  • afterPropertiesSet 是 Spring 特有接口
  • 执行顺序:@PostConstruct 先执行

Q: BeanPostProcessor 和 InitializingBean 的区别?

  • InitializingBean 针对单个 Bean
  • BeanPostProcessor 对所有 Bean 生效

Q: Spring 如何解决循环依赖?

  • 构造器循环依赖:无法解决,报错
  • Setter/字段循环依赖:通过三级缓存解决

理解 Spring Bean 的生命周期,是掌握 Spring 框架的必经之路。希望这篇文章能帮你建立起清晰的理解,在实际开发中更加得心应手。