1、简介 {#1简介}
本文将带你了解 Spring 如何处理 Prototype Bean 并管理其生命周期,主要介绍是否有必要手动销毁 Prototype Bean、何时销毁以及如何销毁。
Spring 提供了多种 Bean Scope,本文主要聚焦 Prototype。
2、Prototype Bean 及其生命周期 {#2prototype-bean-及其生命周期}
Scope(作用域)确定了 Bean 在其存在的上下文中的生命周期和可见性。根据 Scope 的定义 ,IoC 容器负责管理 Bean 的生命周期。Prototype(原型)Scope 指示容器在每次使用 getBean()
请求或注入到另一个 Bean 时创建一个新的 Bean 实例。在创建和初始化方面,可以放心地依赖于 Spring。然而,销毁 Bean 的过程则不同。
在了解销毁 Bean 的必要性之前,先看看如何创建一个 Prototype Bean,如下:
@Component
// 指定 Bean 的 Scope 为 Prototype
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeExample {
}
3、Prototype Bean 需要手动销毁吗? {#3prototype-bean-需要手动销毁吗}
Spring 不会自动销毁 Prototype Bean。与 Singleton Scope 不同,IoC 容器并不负责 Prototype Bean 整个的生命周期。容器会实例化、配置和组装 Prototype Bean,但之后将停止跟踪其状态。
在 Java 中,当一个对象无法再通过任何引用到达时,它就符合垃圾回收(GC)的条件。通常,在使用完 Prototype Bean 实例后,让其等待 GC 即可。换句话说,在大多数使用情况下,不必费心销毁 Prototype Bean。
然后,也要考虑一下需要手动销毁 Bean 的情况。例如,在处理文件、数据库连接或网络等需要资源的流程时。由于 Prototype Scope 规定,我们每次使用 Bean 时都会创建它,这意味着资源也会被使用和消耗。因此,随着时间的推移,使用量的累积可能会导致 内存泄漏 和 连接池耗尽 等潜在问题。之所以会出现这种情况,是因为我们从未释放过这些资源,而是通过使用 Prototype Bean 不断创建新的资源。
这种场景下,我们必须确保在使用 Prototype Bean 后正确地销毁它们,关闭创建或使用的所有资源。
4、如何销毁 Prototype Bean? {#4如何销毁-prototype-bean}
在 Spring 中手动销毁 Bean 有几种方法,你甚至可以同时使用多种方法。
每个示例都需要手动调用 BeanFactory
中的 destroyBean()
方法(除了自定义销毁方法),我们从 ApplicationContext
获取 BeanFactory
并调用 destroyBean
:
applicationContext.getBeanFactory().destroyBean(prototypeBean);
4.1、使用 @PreDestroy 注解 {#41使用-predestroy-注解}
注解 @PreDestroy
用于标记 Bean 中负责销毁 Bean 的方法。方法不允许有任何参数,也不允许是静态的:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PreDestroyBeanExample {
@PreDestroy
private void destroy() {
// 释放 Bean 持有的所有资源
}
}
4.2、DisposableBean 接口 {#42disposablebean-接口}
实现 DisposableBean
接口,它有一个唯一的回调方法 destroy()
:。
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DisposableBeanExample implements DisposableBean {
@Override
public void destroy() {
// 释放 Bean 持有的所有资源
}
}
Spring 团队并不推荐使用
DisposableBean
接口,因为它会将代码耦合到 Spring 中。
4.3、DestructionAwareBeanPostProcessor 接口 {#43destructionawarebeanpostprocessor-接口}
DestructionAwareBeanPostProcessor
(类似于其他 BeanPostProcessor
变体)用于自定义 Bean 的初始化过程。一个关键的区别是它包含一个额外的方法,在销毁 Bean 之前执行自定义逻辑。
在实现该接口之前,我们必须要有一种释放 Bean 资源的方式。我们可以使用DisposableBean
,就像在前面的示例中一样,或者使用自定义方法。
下一步,实现该接口,在其中调用销毁方法:
@Component
public class CustomPostProcessor implements DestructionAwareBeanPostProcessor {
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy();
}
}
}
4.4、使用自定义方法的 POJO {#44使用自定义方法的-pojo}
我们可能会遇到这样一种情况:我们想将一个 POJO 定义为 Prototype Bean。
在定义 Bean 时,我们可以使用 @Bean
注解的 destroyMethod
属性来指定负责销毁 Bean 的特定方法:
public class CustomMethodBeanExample {
public void destroy() {
// 释放 Bean 持有的所有资源
}
}
@Configuration
public class DestroyMethodConfig {
@Bean(destroyMethod = "destroy") // 指定 Bean 的销毁方法
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public CustomMethodBeanExample customMethodBeanExample() {
return new CustomMethodBeanExample();
}
}
我们通过 destroyMethod
属性指定了 Bean 销毁时的自定义方法,但它永远不会被调用。这是因为容器只会对其生命周期完全受其控制的 Bean 调用该方法。在这种情况下,我们可以使用 DestructionAwareBeanPostProcessor
或在停止使用 Prototype Bean 时调用我们的自定义销毁方法。
5、总结 {#5总结}
本文介绍了 Spring 如何处理 Prototype Bean 并管理其生命周期,以及几种可以用来销毁 Bean 的方法,包括 @PreDestroy
、DisposableBean
接口、DestructionAwareBeanPostProcessor
接口和自定义方法。
尽管手动销毁 Prototype(原型)Bean可能并不是必需的,但如果它们处理资源,如文件处理、数据库连接或网络操作,建议这样做。由于每次请求时都会创建 Prototype Bean 实例,资源会迅速累积。为了避免任何不必要的问题,如内存泄漏,我们必须释放资源。
Ref:https://www.baeldung.com/spring-manually-destroy-prototype-bean