SpringNotes

第 03 章:Bean 生命周期

zjc 于 2026-01-03 发布

这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Bean 生命周期描述一个对象从 BeanDefinition 到可使用、再到销毁的完整过程。事务、异步、配置绑定、初始化校验、动态代理都发生在生命周期不同阶段。

3.1 总体流程

BeanDefinition 加载
  -> 实例化前处理
  -> 构造对象
  -> 属性填充
  -> Aware 回调
  -> BeanPostProcessor.before
  -> 初始化回调
  -> BeanPostProcessor.after
  -> Bean 就绪
  -> 容器关闭
  -> 销毁回调

Spring 实际执行细节较复杂,但这条主线非常稳定。

3.2 实例化

常用方式:

  1. 反射调用构造器;
  2. 工厂方法实例化;
  3. Supplier 实例化;
  4. FactoryBean.getObject()

构造器选择:

@Service
public class OrderService {
    private final OrderRepository repository;

    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }
}

如果只有一个构造器,Spring 通常可直接使用。多个构造器时:

  1. 可用 @Autowired 标注首选;
  2. 可用 @ConstructorProperties 配合;
  3. 应尽量保持构造器清晰。

3.3 属性填充

实例化后,Spring 会注入依赖和配置值。

@Component
public class PayClient {
    @Value("${pay.timeout:3s}")
    private Duration timeout;
}

更推荐构造器绑定:

@Component
public class PayClient {
    private final Duration timeout;

    public PayClient(@Value("${pay.timeout:3s}") Duration timeout) {
        this.timeout = timeout;
    }
}

属性填充阶段也可能触发类型转换、SpEL 求值和依赖解析失败。

3.4 Aware 回调

常用 Aware:

接口 获得内容
BeanNameAware Bean 名称
BeanClassLoaderAware 类加载器
BeanFactoryAware BeanFactory
EnvironmentAware Environment
ApplicationContextAware ApplicationContext

示例:

@Component
public class ContextHolder implements ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
    }
}

Aware 会把容器对象带进业务代码,增加耦合。非框架组件应优先使用构造器注入;只有通用基础设施才适合使用 Aware。

3.5 初始化回调

三种常见方式:

@Component
public class CacheLoader implements InitializingBean {
    @Override
    public void afterPropertiesSet() {
        reload();
    }
}

JSR-250:

@PostConstruct
public void init() {
    reload();
}

声明式:

@Bean(initMethod = "start", destroyMethod = "close")
public StorageClient storageClient() {
    return new StorageClient();
}

推荐顺序:

  1. 业务 Bean 优先 @PostConstruct,简单直观;
  2. 第三方对象优先 @Bean(initMethod, destroyMethod),不侵入库代码;
  3. 框架内部才实现 InitializingBean

3.6 BeanPostProcessor

BeanPostProcessor 在初始化前后介入:

@Component
public class AuditBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof Auditable auditable) {
            return Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        System.out.println("before " + method.getName());
                        return method.invoke(bean, args);
                    });
        }
        return bean;
    }
}

典型内置处理器:

处理器 作用
AutowiredAnnotationBeanPostProcessor 解析 @Autowired
CommonAnnotationBeanPostProcessor @PostConstruct@PreDestroy、资源注入
ApplicationContextAwareProcessor Aware 回调
AsyncAnnotationBeanPostProcessor @Async 代理
AbstractAutoProxyCreator AOP 代理

BeanPostProcessor 会影响所有 Bean,应保持轻量、稳定。

3.7 销毁回调

容器关闭时:

发布 ContextClosedEvent
  -> 销毁单例
  -> @PreDestroy
  -> DisposableBean.destroy()
  -> @Bean(destroyMethod)

示例:

@PreDestroy
public void shutdown() {
    scheduler.shutdown();
}

Spring Boot 收到 SIGTERM 后会关闭上下文。若 Java 进程没有收到信号,例如启动脚本没有 exec,销毁回调不会执行。

3.8 作用域

作用域 生命周期
singleton 容器内一个实例
prototype 每次获取创建新实例
request HTTP 请求内
session HTTP 会话内
application ServletContext 内
websocket WebSocket 会话内

singleton Bean 注入 prototype Bean 时,只会注入一次。需要每次新对象时可用:

@Component
public class TaskFactory {
    private final ObjectProvider<TaskWorker> workers;

    public TaskFactory(ObjectProvider<TaskWorker> workers) {
        this.workers = workers;
    }

    public TaskWorker newWorker() {
        return workers.getObject();
    }
}

3.9 代理对生命周期的影响

如果 Bean 需要 AOP,容器最终放入单例池的可能是代理对象。

目标对象
  -> 属性填充
  -> 初始化
  -> AbstractAutoProxyCreator
  -> 代理对象
  -> 容器对外暴露代理

常见影响:

  1. 注入类型可能是代理类型;
  2. @Transactional 自调用失效;
  3. final 类或方法代理失败;
  4. CGLIB 代理需要可访问构造器;
  5. 在构造器中使用依赖可能拿到 null。

3.10 初始化中的阻塞问题

错误做法:

@PostConstruct
public void init() {
    // 远程加载大配置,可能耗时 30s
    remoteConfig.loadAll();
}

后果:

  1. 应用启动慢;
  2. 健康检查误判;
  3. 发布滚动阻塞;
  4. 远程故障导致启动失败。

建议:

  1. 启动只加载必要数据;
  2. 异步预热后台执行;
  3. 使用就绪探针区分启动和可服务;
  4. 外部调用设置超时和重试;
  5. 首次加载失败允许降级。

3.11 调试生命周期

开启 Bean 创建日志:

logging.level.org.springframework.beans.factory=DEBUG
logging.level.org.springframework.context=DEBUG

断点位置:

问题 断点
Bean 未创建 AbstractBeanFactory.getBean
依赖注入失败 AbstractAutowireCapableBeanFactory.populateBean
初始化失败 initializeBean
AOP 没生效 AbstractAutoProxyCreator
循环依赖 DefaultSingletonBeanRegistry

也可以自定义处理器打印关键阶段:

@Component
public class BeanLifecycleLogger implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (beanName.startsWith("order")) {
            System.out.println("before init: " + beanName);
        }
        return bean;
    }
}

本章小结

Bean 生命周期由实例化、属性填充、Aware 回调、初始化前后处理、初始化方法、代理生成和销毁回调组成。BeanPostProcessor 是 Spring 扩展体系的核心,也是自动注入和 AOP 的实现基础。业务代码应优先使用清晰的构造器注入和 @PostConstruct,避免初始化阶段做长时间阻塞操作。

思考题

  1. @PostConstructafterPropertiesSet 谁先执行?
  2. BeanPostProcessor 能否修改 BeanDefinition?
  3. singleton 注入 prototype 为什么只创建一次?
  4. 为什么初始化方法中不适合调用慢速远程服务?
  5. AOP 代理是在生命周期哪个阶段生成的?