面试记录 2026-09-28
题目:Spring Bean的生命周期是怎么样的?
难度:difficulty/medium
标签:spring/bean, general/knowledge, difficulty/medium
评分:⭐⭐⭐☆☆(3/5)
我的回答
Spring Bean 的生命周期,初始化、实例化、常驻使用中、在缓存中复用、最终销毁回收,基本上就是它整个生命周期了。按照佛教的说法就是成住坏空;
这也是常规对象的所有生命周期,而 Spring 中不同的就在于这个整个生命周期都在 Spring 的自己的管理之下。你的对象创建、对象的实例化都是由 Spring 进行的,包括它的缓存、它的销毁,都是 Spring 来进行操作的。
Spring 先去根据注解找到要注入的对象,比如,@Component、@Service,这些被标注的对象,Spring 会将其实例化,并把对象缓存到对应的缓存中。实例完后会缓存到一级缓存,创建过程中可能会在二级或者三级缓存中,二级缓存中存留一会,三级缓存中存的是工厂。完成了实例化之后,开始通过缓存去进行使用调用。要使用的时候就通过 对应的注释读取对应的 bean,进行使用,对象销毁时,如果要进行一些处理,可以在对象的销毁方法中进行自己想要实现的业务逻辑,在销毁时会进行调用执行。
点评
优点:
- 抓住了生命周期的宏观阶段:实例化 → 初始化 → 使用 → 销毁
- 提到了三级缓存机制和 singleton 作用域
- 提到 destroy 方法可以做清理
不足:
- 完全没展开具体的生命周期回调方法,这是本题的核心考点。缺了三类核心钩子:
- BeanPostProcessor(最关键的抽象,Spring 中 AOP、事务、@PostConstruct 处理都靠它):postProcessBeforeInitialization / postProcessAfterInitialization
- 初始化阶段:@PostConstruct → InitializingBean.afterPropertiesSet() → 自定义 init-method(三者顺序)
- 销毁阶段:@PreDestroy → DisposableBean.destroy() → 自定义 destroy-method
- 实例化的关键步骤没提:BeanDefinition 加载 → 校验 → 实例化(createBeanInstance)→ 属性填充(populateBean)→ 初始化(initializeBean)
- 提到三级缓存后没有解释三级缓存介入生命周期的具体时机
- 顺序容易记混:@PostConstruct → afterPropertiesSet → init-method;销毁反过来:@PreDestroy → destroy → destroy-method
补充:
标准生命周期骨架:
- BeanDefinition 加载
- 实例化 createBeanInstance
- 将 bean 放入三级缓存 addSingletonFactory
- 属性填充 populateBean(包括依赖注入)
- Aware 接口回调(BeanNameAware / BeanFactoryAware / BeanClassLoaderAware)
- BeanPostProcessor.postProcessBeforeInitialization() → 内部调用 @PostConstruct
- 初始化方法执行(依次):@PostConstruct → InitializingBean.afterPropertiesSet() → 自定义 init-method
- BeanPostProcessor.postProcessAfterInitialization() → AOP 代理通常在此创建
- 放入一级缓存 singletonObjects
- 使用中
- 销毁:@PreDestroy → DisposableBean.destroy() → 自定义 destroy-method
记忆口诀:实例化 → 属性填充 → Aware → 前置钩子 → 初始化 → 后置钩子 → 使用 → 销毁。
完整讲解
第一部分:知识重点剖析
核心考点定位:考察对 Spring IoC 容器 Bean 完整创建流程的深度理解,特别是三类核心回调接口(BeanPostProcessor / Aware / InitializingBean)的介入时机。
知识点拆解:
- 五个阶段总览:
- 加载配置(BeanDefinition 元数据解析)
- 实例化(调用构造器 createBeanInstance)
- 属性填充(populateBean,处理依赖注入)
- 初始化(Aware 回调 + BeanPostProcessor 前后钩子 + 初始化方法)
- 使用与销毁
- 三个核心抽象接口:
- BeanDefinitionRegistryPostProcessor:修改 BeanDefinition(在实例化前),如 @EnableAspectJAutoProxy 注册切面
- BeanPostProcessor:修改 Bean 实例(在初始化前后),Spring 里 90% 高级功能都靠它:AOP、@Autowired、@Resource、@PostConstruct、@EventListener、@Value
- Aware 接口族:让 Bean 感知容器上下文(BeanNameAware、BeanFactoryAware、ApplicationContextAware 等)
- 三级缓存在生命周期中的位置:
- 实例化后立即放 L3(addSingletonFactory),让循环依赖能提前拿到代理对象
- 属性填充阶段可能从 L2/L3 获取代理对象
- 完全初始化后放 L1,清理 L2/L3
- 初始化三兄弟严格顺序(必背):
- @PostConstruct(CommonAnnotationBeanPostProcessor 处理)
- InitializingBean.afterPropertiesSet()
- 自定义 init-method(@Bean(initMethod="x") 或 XML init-method="x")
- 销毁三兄弟顺序(与初始化反向):
- @PreDestroy
- DisposableBean.destroy()
- 自定义 destroy-method
- 不同作用域的差异:
- singleton:容器启动时创建一次,容器关闭时销毁一次
- prototype:每次 getBean 都新建,Spring 不管理其销毁
- request/session:随 Web 请求/会话生灭
第二部分:考察方向与延伸递进
考察方向:
- 深度:BeanPostProcessor 机制、三级缓存介入时机
- 广度:AOP 代理生成时机、@PostConstruct 与 init-method 的关系
- 应用:自定义 BeanPostProcessor、生产环境初始化资源加载
延伸递进面试题:
- @Autowired 什么时候被注入?本质是什么?
- 解答要点:属性填充阶段(populateBean)由 AutowiredAnnotationBeanPostProcessor 处理,本质是 BeanPostProcessor;@Resource 也是同类机制
- AOP 代理对象什么时候生成?如果发生循环依赖呢?
- 解答要点:正常流程在 postProcessAfterInitialization(AbstractAutoProxyCreator);循环依赖场景下代理会在更早的 L3 ObjectFactory 调用时提前生成,保证 B 拿到 A 的代理而非原始对象
- @PostConstruct 和 init-method 谁先执行?为什么这样设计?
- 解答要点:@PostConstruct 先。三者顺序:@PostConstruct → afterPropertiesSet → init-method。设计初衷:注解优先于接口(更解耦、无需实现接口)、接口优先于 XML 配置(更类型安全)
第三部分:面试者参考回答(300-600 字口语化)
面试官您好,Spring Bean 的完整生命周期我一般会分五个阶段来讲。
第一阶段是加载配置:Spring 通过 XML、@ComponentScan 或 Java Config 扫描出类,解析成 BeanDefinition 元数据。这个阶段 BeanFactoryPostProcessor 会介入,比如处理 ${xxx} 占位符替换。
第二阶段是实例化:调用构造器生成 bean 实例,此时对象刚创建但还没初始化属性。关键点:实例化后 Spring 会立刻把这个 bean 的 ObjectFactory 放进三级缓存,为后面的循环依赖做准备。
第三阶段是属性填充:处理 @Autowired、@Resource、setter 注入,本质都是 AutowiredAnnotationBeanPostProcessor 这个 BeanPostProcessor 在工作。
第四阶段是初始化,这是最复杂的:
- 先走 Aware 接口(BeanNameAware、ApplicationContextAware 等),让 bean 感知容器
- 然后 BeanPostProcessor.postProcessBeforeInitialization 被调用,CommonAnnotationBeanPostProcessor 在这里处理 @PostConstruct
- 接着按严格顺序执行三兄弟:@PostConstruct → InitializingBean.afterPropertiesSet() → 自定义 init-method
- 最后 BeanPostProcessor.postProcessAfterInitialization 被调用,AOP 代理就在这个环节由 AbstractAutoProxyCreator 创建
第五阶段是缓存使用:完全初始化后 bean 被放入一级缓存 singletonObjects,singleton 作用域的 bean 会被容器复用直到关闭。
销毁阶段反过来走三步:@PreDestroy → DisposableBean.destroy() → 自定义 destroy-method。
几个关键点补充:
- 90% 的 Spring 高级功能(AOP、@Autowired、@PostConstruct、@EventListener)都是通过 BeanPostProcessor 机制实现的
- 三级缓存介入的生命周期位置在属性填充之前,目的是让 AOP 场景下的循环依赖能提前暴露代理对象
- prototype 作用域的 bean 每次 getBean 都新建,且 Spring 不管其销毁,循环依赖也完全无法解决
实际开发中我最常用的自定义时机是 InitializingBean.afterPropertiesSet,比如初始化一些静态资源、注册事件监听器;销毁时用 @PreDestroy 关闭资源。