Spring Bean 的生命周期是怎么样的?

#Java#面试八股

Spring Bean 的生命周期是怎么样的?

结论先行:单例 Bean 的生命周期可以压成一条主线——实例化 → 属性填充 → Aware 回调 → 前置处理 → 初始化 → 后置处理(此处生成 AOP 代理)→ 使用 → 销毁。面试要能说清三个顺序:@PostConstruct 早于 afterPropertiesSet,早于自定义 init-method;BeanPostProcessor#postProcessAfterInitialization 是 AOP 代理诞生的地方;prototype Bean 容器不管销毁。

一、完整流程

① 读取 BeanDefinition(扫描/配置类解析)
        │
② 实例化 instantiate         ← 构造器、Supplier、FactoryBean#getObject
        │
③ 属性填充 populateBean      ← @Autowired / @Value 注入,依赖在此刻被拉取
        │
④ Aware 回调                 ← BeanNameAware → BeanClassLoaderAware → BeanFactoryAware
        │
⑤ BeanPostProcessor.postProcessBeforeInitialization
        │
⑥ 初始化
     @PostConstruct
     InitializingBean#afterPropertiesSet
     自定义 init-method
        │
⑦ BeanPostProcessor.postProcessAfterInitialization   ← AOP 代理在此生成
        │
⑧ 就绪,放入 singletonObjects,可被使用
        │
⑨ 容器关闭:@PreDestroy → DisposableBean#destroy → destroy-method

二、扩展点归属

阶段接口/注解备注
实例化前InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation可返回代理,直接短路后续流程
属性填充前postProcessAfterInstantiation返回 false 可跳过注入
属性填充postProcessPropertyValues / @Autowired依赖注入发生点
AwareBeanNameAware、BeanFactoryAware、ApplicationContextAware让 Bean 拿到容器自身信息
初始化前postProcessBeforeInitialization@PostConstruct 也在此阶段被调用
初始化InitializingBean、init-method顺序固定:注解 → 接口 → 配置
初始化后postProcessAfterInitializationAOP 代理、@Async 代理生成
销毁@PreDestroy → DisposableBean → destroy-method仅单例由容器回调

三、代码验证顺序

@Component
public class LifecycleBean implements InitializingBean, DisposableBean, BeanNameAware {

    public LifecycleBean() {
        System.out.println("1 构造器");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("3 @PostConstruct");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("4 afterPropertiesSet");
    }

    public void initMethod() {
        System.out.println("5 init-method");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("7 @PreDestroy");
    }

    @Override
    public void destroy() {
        System.out.println("8 destroy");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("2 setBeanName(Aware)");
    }
}
@Configuration
public class LifecycleConfig {
    // 输出顺序:1 → 2 → 3 → 4 → 5 → (使用) → 7 → 8
    @Bean(initMethod = "initMethod", destroyMethod = "destroy")
    public LifecycleBean lifecycleBean() {
        return new LifecycleBean();
    }
}

四、AOP 代理的位置很关键

AbstractAutowireCapableBeanFactory#initializeBean 的末尾会调用 applyBeanPostProcessorsAfterInitialization,AnnotationAwareAspectJAutoProxyCreator 就在这里返回代理对象。所以:

  • 初始化方法里 this 仍指向原始对象,this.getClass() 看不到代理;
  • 注入到其他 Bean 的是代理,因此事务、缓存、异步注解能生效;
  • @PostConstruct 中调用本类的 @Transactional 方法不会走代理,事务不生效——这是"初始化里调事务方法失败"的根因。

五、常见追问

  • 问:BeanPostProcessor 和 BeanFactoryPostProcessor 区别? 前者处理"Bean 实例",后者处理"BeanDefinition",且后者执行更早(refresh() 的 invokeBeanFactoryPostProcessors 阶段)。
  • 问:prototype Bean 会走销毁流程吗? 不会,容器不保存其引用,@PreDestroy 不触发,需自行释放。
  • 问:循环依赖发生在哪一步? 属性填充(③)阶段:A 填充时发现依赖 B,B 填充时又发现依赖 A,靠三级缓存提前暴露引用解决。
  • 问:@PostConstruct 在 JDK 11+ 找不到注解? 需引入 jakarta.annotation-api;Spring Boot 3 已统一为 jakarta.annotation.PostConstruct。

小结

背生命周期不要背十步名词,背"四段":造出来(实例化 + 填充 + Aware)、初始化(三个回调按序)、包一层(BPP 后置处理生成代理)、拆掉(销毁三个回调)。再把 AOP 代理位置和 prototype 不销毁两个细节补上,答案就有区分度了。

笔记加载中…