🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪
🍅 技术交流:定期更新Java硬核干货,不定期送书活动
🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试
🍅 数十万人的面试选择: 面试说人话系列《面试1v1》
我是 javapub,一名 Markdown
程序员从👨💻,八股文种子选手。
《面试1v1》 连载中…
面试官: 小伙子,听说你对 Spring Bean 生命周期比较熟悉,我们聊聊吧。Spring Bean 都有哪些生命周期阶段?
候选人: Spring Bean 的生命周期可以分为 5 个阶段:
- 实例化(Instantiation):Spring 使用 BeanDefinition 中的信息实例化 Bean。
- 属性赋值(Dependency injection):Spring 将 BeanDefinition 中配置的属性值注入到 Bean 中。
- 初始化前阶段(Post-Construct):如果 Bean 实现了 InitializingBean 接口,会调用 afterPropertiesSet() 方法。
- 初始化阶段(Initialization):如果在 BeanDefinition 中配置了 init-method,会调用该方法。
- 销毁阶段(Destruction):如果 Bean 实现了 DisposableBean 接口,会调用 destroy() 方法。如果配置了 destroy-method,会调用该方法。
面试官: 聪明!初始化方法有哪些?在源码层面,Spring 是如何调用这些方法的?
候选人: Spring Bean 提供了 3 种初始化方法:
- @PostConstruct:这是 JSR-250 注解,Spring 会在 Bean 初始化后自动调用被此注解标注的方法。
- InitializingBean 接口:这个接口只有一个方法 afterPropertiesSet(),Spring 会在 Bean 初始化后调用该方法。
- 自定义 init-method:在 BeanDefinition 中配置 init-method 属性,指向 Bean 中的某个方法名,Spring 会在 Bean 初始化后调用这个方法。
在源码层面,这些方法的调用是在 AbstractAutowireCapableBeanFactory
的 initializeBean
方法中实现的:
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// ...
// 1. 处理 PostConstruct 注解
if (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet")) {
// 2. 实现了 InitializingBean 接口的 Bean 会调用 afterPropertiesSet() 方法
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
}
// 3. 调用自定义的 init-method
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
Method initMethod = bean.getClass().getMethod(initMethodName);
initMethod.invoke(bean);
}
}
}
面试官: 不错,你对 Spring Bean 的初始化过程很清楚!那销毁方法哪些?原理又是什么?
候选人: Spring Bean 提供了 2 种销毁方法:
- DisposableBean 接口:实现这个接口的 Bean 会调用 destroy() 方法。
- 自定义 destroy-method:在 BeanDefinition 中配置 destroy-method 属性,指向 Bean 中的某个方法名,Spring 会在 Bean 销毁前调用这个方法。
在源码层面,这些方法的调用是在 AbstractAutowireCapableBeanFactory
的 destroyBean
方法中实现的:
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
// 1. 实现了 DisposableBean 接口的 Bean 会调用 destroy() 方法
if (bean != null) {
bean.destroy();
}
// 2. 调用自定义的 destroy-method
String destroyMethodName = getDestroyMethodName(beanName);
if (destroyMethodName != null) {
Method destroyMethod = null;
try {
// 获取 destroy-method 方法对象
destroyMethod = bean.getClass().getMethod(destroyMethodName);
} catch (NoSuchMethodException ex) {
throw new BeanDefinitionStoreException(...);
}
// 调用方法
try {
if (destroyMethod != null) {
destroyMethod.invoke(bean);
}
} catch (...) {
throw new BeanCreationException(...);
}
}
}
面试官: 棒!最后,Spring Bean 的作用域都有哪些?如何控制 Bean 的生命周期?
候选人: Spring Bean 的作用域有 5 种:
- singleton:单例,整个 Spring 容器中只有一个 Bean 实例。
- prototype:原型,每次获取 Bean 都会创建一个新的实例。
- request:每个 HTTP 请求都会创建一个 Bean 实例。
- session:每个 HTTP 会话都会创建一个 Bean 实例。
- global-session:每个全局 HTTP 会话都会创建一个 Bean 实例。
我们可以通过 scope
属性控制 Bean 的作用域,从而影响其生命周期:
<bean id="..." class="..." scope="prototype"/>
此外,我们还可以自定义 Bean 的初始化和销毁方法,在 Bean 作用域开始和结束时触发:
<bean id="..." class="..." scope="prototype"
init-method="start" destroy-method="end">
</bean>
这样我们就可以在 start()
方法中执行初始化逻辑,在 end()
方法中执行清理工作,从而精确控制 Bean 的生命周期。
面试官: 很全面,佩服佩服!如果再给你一个机会,你觉得还可以在哪些方面加深对 Spring Bean 生命周期的理解?
候选人: 这里有几个方面可以进一步加深对 Spring Bean 生命周期的理解:
- BeanPostProcessor:这个接口可以监听 Bean 的初始化前后,提供了扩展点可以在 Bean 初始化前后进行一些处理。这也是 Spring AOP 的底层原理之一。
- 了解 BeanFactoryPostProcessor:这个接口可以监听 BeanDefinition 的加载,可以在 Bean 实例化前修改 BeanDefinition 的属性。
- 理解 Bean 的加载时机:在 Spring 容器启动时,默认会立即加载 singleton 作用域的 Bean,而其他作用域的 Bean 会延迟加载, singleton 作用域的 Bean 也支持延迟加载。这就要涉及到Spring 的
lazy-init
属性设置。 - 了解 Bean 为什么要有不同的作用域:每个作用域适合的场景是什么,选择不同作用域会对 Bean 的生命周期产生怎样的影响。
- 了解 Bean 之间的依赖关系对生命周期的影响:比如 A Bean 的初始化依赖 B Bean,那么 A Bean 的初始化也会延迟到 B Bean 初始化完毕后。这涉及到 Spring 的
depends-on
属性配置。 - 了解自定义初始化和销毁方法的具体应用场景:什么情况下需要自定义这些方法,能在方法中完成什么样的逻辑处理。
- 探索 BeanPostProcessor 和 BeanFactoryPostProcessor 的具体应用:比如 Spring AOP、Spring 事件发布者等机制的实现。
综上,要全面理解 Spring Bean 的生命周期,除了知道每个阶段的调用外,还需要对很多这个过程涉及到的其他知识点进行深入学习和理解,这需要不断实践和总结。但只要把这些要点都串联起来,对 Spring Bean 的生命周期控制就会很得心应手了。
面试官: 非常棒,这些点精彩极了!你的回答已经很全面和深入,对 Spring Bean 生命周期有清晰理解,这些又是常见的面试重点,我相信面试一定会取得很好的表现,加油!我们就聊到这里,很高兴与你的交流,谢谢!
最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解我们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣可以关注JavaPub追更!
《面试1v1》 连载中…
🎁目录合集:
Gitee:https://gitee.com/rodert/JavaPub
GitHub:https://github.com/Rodert/JavaPub
http://javapub.net.cn