51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Spring Prototype Bean 需要手动销毁吗?

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 的方法,包括 @PreDestroyDisposableBean 接口、DestructionAwareBeanPostProcessor 接口和自定义方法。

尽管手动销毁 Prototype(原型)Bean可能并不是必需的,但如果它们处理资源,如文件处理、数据库连接或网络操作,建议这样做。由于每次请求时都会创建 Prototype Bean 实例,资源会迅速累积。为了避免任何不必要的问题,如内存泄漏,我们必须释放资源。


Ref:https://www.baeldung.com/spring-manually-destroy-prototype-bean

赞(1)
未经允许不得转载:工具盒子 » Spring Prototype Bean 需要手动销毁吗?