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


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 流程掌握度:面试官不仅仅是想知道你能背出几个步骤,更是想看你能不能把 "实例化 → 属性注入 → 初始化 → 销毁" 这条主线讲清楚,而不是东一句西一句。

  2. 扩展点认知:是否了解 BeanPostProcessorAware 接口、InitializingBean 等关键扩展点,知道它们在生命周期中的执行顺序和实际用途。这才是区分 "背了八股文" 和 "真正用过 Spring" 的分水岭。

  3. 源码级理解:高级岗位可能会追问到 AbstractApplicationContext#refresh()AbstractAutowireCapableBeanFactory#doCreateBean() 的源码流程,看你是不是看过源码。

核心答案

一句话概括:实例化 → 属性注入 → Aware 回调 → BeanPostProcessor 前置处理 → 初始化 → BeanPostProcessor 后置处理 → 使用 → 销毁

完整流程可以分为 4 个阶段7 个核心步骤——

上图展示了 Bean 从生到死的完整旅程。下面分阶段拆解,每一步都不放过。

深度解析

一、实例化 — Bean 的 "出生"

Spring 通过反射调用类的构造函数,创建出一个对象实例。注意,这时候的对象还只是个 "半成品",属性都是 null,依赖一个都没注入。

// Spring 内部大致是这样做的
Class<?> clazz = Class.forName("com.example.UserService");
Object instance = clazz.getDeclaredConstructor().newInstance();

实例化的时机跟 Bean 的作用域有关:singleton Bean 在容器启动时(或首次被请求时)创建,prototype Bean 每次获取时创建。

二、属性注入 — 给 "半成品" 装配零件

实例化之后,Spring 开始给这个对象注入依赖和属性值,包括 @Autowired@Value@Resource 等注解标注的字段或方法,以及 XML 中配置的 property。

@Component
public class OrderService {

    @Autowired
    private UserService userService;  // 这一步被注入

    @Value("${order.timeout}")
    private int timeout;  // 这一步被注入
}

如果存在循环依赖(A 依赖 B,B 又依赖 A),Spring 通过三级缓存来解决 singleton 场景下的循环依赖。这个后面会单独出一篇讲,这里先不展开。

三、Aware 接口回调 — 让 Bean 知道 "自己在哪"

如果 Bean 实现了 Spring 提供的各种 Aware 接口,Spring 会在这个阶段把对应的资源注入进去。

@Component
public class MyBean implements BeanNameAware, BeanFactoryAware,
                                ApplicationContextAware {

    @Override
    public void setBeanName(String name) {
        // 拿到自己在容器中的名字
        System.out.println("我的名字是:" + name);
    }

    @Override
    public void setBeanFactory(BeanFactory factory) {
        // 拿到 BeanFactory 引用
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        // 拿到 ApplicationContext 引用
    }
}

常用的 Aware 接口:

Aware 接口 注入的资源
BeanNameAware Bean 的名称
BeanFactoryAware BeanFactory 实例
ApplicationContextAware ApplicationContext 实例
EnvironmentAware Environment 实例
ResourceLoaderAware ResourceLoader 实例

说实话,日常开发中直接实现 Aware 接口的场景不多,但面试爱问。ApplicationContextAware 偶尔会用来在非 Spring 管理的类中获取 Bean。

四、BeanPostProcessor — 最强的扩展点

BeanPostProcessor 是 Spring 留给我们的最强大的扩展机制,AOP 代理、@PostConstruct 注解的处理、属性校验等都是通过它实现的

public interface BeanPostProcessor {

    // 初始化之前调用
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    // 初始化之后调用(AOP 代理通常在这步生成)
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

关键点:BeanPostProcessor 对容器中所有 Bean 都生效,不是一个 Bean 专属的。Spring 内置了很多 BeanPostProcessor,比如:

  • CommonAnnotationBeanPostProcessor:处理 @PostConstruct@PreDestroy
  • AutowiredAnnotationBeanPostProcessor:处理 @Autowired@Value
  • AbstractAutoProxyCreator:处理 AOP 代理

没错,@PostConstruct 的执行时机是在 postProcessBeforeInitialization 阶段,由 InitDestroyAnnotationBeanPostProcessor 负责调用。很多人以为 @PostConstruct 是在 "初始化" 阶段执行,严格来说是初始化之前的 "前置处理" 阶段。

五、初始化 — Bean 的 "成人礼"

属性注入完成后,Bean 可以执行一些自定义的初始化逻辑。有三种方式,执行顺序如下:

@Component
public class MyBean implements InitializingBean {

    // 第 1 个执行:@PostConstruct 注解方法
    @PostConstruct
    public void postConstruct() {
        System.out.println("1. @PostConstruct 执行");
    }

    // 第 2 个执行:InitializingBean 接口方法
    @Override
    public void afterPropertiesSet() {
        System.out.println("2. afterPropertiesSet() 执行");
    }

    // 第 3 个执行:自定义 init-method
    public void customInit() {
        System.out.println("3. 自定义 init-method 执行");
    }
}
@Configuration
public class AppConfig {
    @Bean(initMethod = "customInit")
    public MyBean myBean() {
        return new MyBean();
    }
}

三种方式的执行顺序固定:@PostConstructafterPropertiesSet()init-method。设计上给了你三个层次的选择,日常用 @PostConstruct 就够了。

六、销毁 — Bean 的 "善终"

容器关闭时(比如调用 ApplicationContext.close()),singleton Bean 会被销毁。销毁顺序和初始化顺序类似,也有三种方式:

@Component
public class MyBean implements DisposableBean {

    // 第 1 个执行
    @PreDestroy
    public void preDestroy() {
        System.out.println("1. @PreDestroy 执行");
    }

    // 第 2 个执行
    @Override
    public void destroy() {
        System.out.println("2. DisposableBean#destroy() 执行");
    }

    // 第 3 个执行
    public void customDestroy() {
        System.out.println("3. 自定义 destroy-method 执行");
    }
}

顺序同样是固定的:@PreDestroydestroy()destroy-method

注意,prototype 作用域的 Bean,Spring 不会调用销毁回调。创建之后就撒手不管了,生命周期由你自己管理。这个坑不少人踩过。

七、完整代码验证

用一个完整的例子把所有步骤跑一遍:

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

    public LifecycleBean() {
        System.out.println("1. 构造函数:实例化");
    }

    @Autowired
    public void setDependency(ApplicationContext ctx) {
        System.out.println("2. 属性注入:@Autowired");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("3. BeanNameAware:beanName = " + name);
    }

    @Override
    public void setBeanFactory(BeanFactory factory) {
        System.out.println("4. BeanFactoryAware");
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        System.out.println("5. ApplicationContextAware");
    }

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

    @Override
    public void afterPropertiesSet() {
        System.out.println("7. InitializingBean#afterPropertiesSet()");
    }

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

    @Override
    public void destroy() {
        System.out.println("9. DisposableBean#destroy()");
    }
}

面试高频追问

  1. 追问一:BeanPostProcessorBeanFactoryPostProcessor 的区别?

    执行时机和作用对象完全不同。BeanFactoryPostProcessorBean 实例化之前 执行,操作的是 BeanDefinition(Bean 的元数据),比如修改属性值、改变作用域。BeanPostProcessorBean 实例化之后 执行,操作的是 Bean 实例本身。一个改图纸,一个改零件。

  2. 追问二:构造函数注入和 @Autowired 字段注入,在生命周期中的执行顺序是什么?

    构造函数注入发生在实例化阶段(第 1 步),是最早执行的。@Autowired 字段注入发生在属性注入阶段(第 2 步)。所以构造函数里拿不到被 @Autowired 标注的字段值——因为还没注入呢。这也是为什么 Spring 官方推荐构造器注入,因为它能在对象创建时就保证依赖完整。

  3. 追问三:Bean 的创建和 AOP 代理的关系是什么?

    AOP 代理通常在 BeanPostProcessor#postProcessAfterInitialization 阶段生成。也就是说,Spring 先创建原始 Bean,走完前面的流程,最后再把它包装成代理对象。但如果 Bean 实现了 SmartInstantiationAwareBeanPostProcessor(比如循环依赖场景),可能会提前创建代理,这涉及三级缓存的机制。

常见面试变体

  • "描述一下 Spring Bean 从创建到销毁的完整过程"
  • "@PostConstructInitializingBean 的执行顺序是什么?"
  • "BeanPostProcessor 在 Bean 生命周期中的作用是什么?"
  • "Spring 有哪些扩展点可以介入 Bean 的创建过程?"

记忆口诀

四大阶段:实例化 → 属性注入 → 初始化 → 销毁

初始化三件套@PostConstructafterPropertiesSet()init-method

销毁三件套@PreDestroydestroy()destroy-method

一句话记:构造生,注入养,Aware 让它认识爹娘,前后置处理器给它打扮,初始化让它长大,用完了再销毁收场。

总结

Bean 生命周期看起来步骤多,但核心就是四件事:创建、注入、初始化、销毁。面试的时候先把这四个阶段说清楚,再补充 BeanPostProcessorAware 接口这些扩展点,基本满分。如果能说出 BeanPostProcessorBeanFactoryPostProcessor 的区别,以及 AOP 代理在生命周期中的介入时机,面试官会觉得你是真正看过源码的。