[面试题] 说一下Spring Bean的生命周期是怎么样的?
📖 版块 A:知识讲解版 (温故知新)
Spring Bean 的生命周期可以高度概括为四个阶段:实例化 (Instantiation) -> 属性赋值 (Populate) -> 初始化 (Initialization) -> 销毁 (Destruction)。
-
实例化 (Instantiation):
- Spring 通过反射机制,根据 BeanDefinition 创建 Bean 实例(此时仅仅是在堆内存中开辟了空间,属性还是默认值)。
-
属性赋值 (Populate):
- Spring 容器根据配置(@Autowired、@Value等)进行依赖注入,填充 Bean 的各项属性。
-
初始化 (Initialization):
- Aware 接口回调:如果 Bean 实现了 BeanNameAware、BeanFactoryAware 等接口,Spring 会将对应的 ID 或工厂实例注入进去。
- BeanPostProcessor 前置处理:执行
postProcessBeforeInitialization方法。 - 初始化方法:执行
@PostConstruct注解的方法、实现InitializingBean接口的afterPropertiesSet方法,以及配置的init-method。 - BeanPostProcessor 后置处理:执行
postProcessAfterInitialization方法。注意:AOP 的代理对象通常是在这一步生成的。
-
销毁 (Destruction):
- 当容器关闭时,执行
@PreDestroy、实现DisposableBean接口的destroy方法,以及配置的destroy-method。
- 当容器关闭时,执行
🎤 版块 B:面试实战版 (高手逐字稿)
Q:说一下 Spring Bean 的生命周期是怎么样的?
💬 高手逐字稿:
关于 Spring Bean 的生命周期,我通常将其划分为四个核心阶段:实例化、属性填充、初始化、以及销毁。
首先是实例化阶段,Spring 容器会通过反射调用构造函数,在堆内存中把对象创建出来。这时候的 Bean 只是一个“半成品”,属性还没赋值。
接着是属性填充阶段,Spring 会扫描所有的注解或者 XML 配置,把这个 Bean 依赖的其他对象(比如用 @Autowired 标注的)或者基础配置值(@Value)注入进去。这一步也是解决循环依赖的关键时机。
然后进入最关键的初始化阶段。这里有很多扩展点,流程是:先执行各种 Aware 接口来让 Bean 感知到容器环境(比如拿到 BeanName 或者 BeanFactory);接着是 BeanPostProcessor 的前置处理;再之后会执行我们自定义的初始化逻辑,比如实现 InitializingBean 接口或者加了 @PostConstruct 注解的方法。
这里有一个非常重要的细节:在初始化阶段的最后一步,也就是 BeanPostProcessor 的后置处理方法中,Spring 会判断这个 Bean 是否需要进行 AOP 增强。如果需要,Spring 会在这里创建并返回一个代理对象(Proxy),而不是原始对象。这也是 Spring 能够实现声明式事务和日志拦截的核心原理。
最后就是销毁阶段,当 Spring 容器关闭时,会触发销毁回调,用于释放资源,比如关闭数据库连接池。
自问自答(进阶细节): 这时候可能会产生一个疑问:如果发生了循环依赖,AOP 代理还是在初始化最后一步生成的吗?其实这里 Spring 做了一个特殊的处理。如果发生了循环依赖,Spring 会通过三级缓存中的 ObjectFactory,提前在属性填充阶段就触发 BeanPostProcessor 来生成代理对象,并把代理对象存入二级缓存。这样就保证了被注入的其他 Bean 拿到的直接就是代理后的对象,而不是原始对象。
所以总结来说,Spring Bean 的生命周期设计得非常精妙,它通过大量的“钩子函数”和后置处理器,让我们可以不侵入源码地在各个阶段对 Bean 进行增强和定制。