14

我有一个 Spring bean 定义beans.xml如下:

<context:annotation-config />
[...]
<bean id="myBackend" class="mycompany.BackendBean" scope="singleton" />

bean 内部有 2 个方法,必须在 Web 应用程序开始和终止之前执行:

public class BackendBean implements IBackend {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(BackendBean.class);

    @PostConstruct
    public void init()
    {
        LOGGER.debug("init");
    }

    @PreDestroy
    public void destroy()
    {
        LOGGER.debug("destroy");
    }
}

当我mvn jetty:run运行init服务器init

当我按下Ctrl-CJetty 开始关闭时,我看不到该destroy方法的输出。

destroy当应用程序终止时,为了执行该方法,我应该更改什么?

4

7 回答 7

16

要让 Spring@PreDestroy在您的应用程序关闭时调用回调方法,您必须添加一个关闭挂钩并关闭它所在的应用程序上下文。Runtime.getRuntime().addShutdownHook(Thread)如果它提供了这样的 API,您可以使用 JVM 或 Jetty 将挂钩附加到。以下是使用 JVM 关闭挂钩的方法:

final ApplicationContext appContext = ... // create your application context 
                         // using one of the various application context classes
Runtime.getRuntime().addShutdownHook(new Thread() {
   public void run() {
       appContext.close();
   }});
于 2013-05-04T10:56:16.993 回答
6

这是使用“原型”作用域 bean 时需要注意的一个微妙点。

对于“原型”范围的 bean,Spring 不调用 @PreDestroy 方法。

以下是 Spring 官方参考手册中的答案:

第 1.5.2 节(见此处

与其他 scope 相比,Spring 不管理原型 bean 的完整生命周期**:容器实例化、配置和以其他方式组装原型对象,并将其交给客户端,没有进一步记录该原型实例.

因此,尽管初始化生命周期回调方法在所有对象上都被调用而不考虑范围,但在原型的情况下,配置的销毁生命周期回调不会被调用。客户端代码必须清理原型范围的对象并释放原型 bean 持有的昂贵资源。

要让 Spring 容器释放原型范围 bean 持有的资源,请尝试使用自定义 bean 后处理器,它包含对需要清理的 bean 的引用。

注意:这也适用于 XML 配置。

于 2019-12-15T10:32:21.127 回答
5

当您@Scope("prototype")在课堂上使用 @PreDestroy 时,即使您尝试关闭context.close();context.registerShutdownHook();

于 2019-03-14T05:29:50.717 回答
2

一个老问题,但想分享我发现的一些东西。

我有一段类似的代码,最初认为 @PreDestroy 方法没有被调用。但后来我在 LOGGER info() 中添加了一个打印语句,并惊讶地发现打印已执行。显然这是因为即使在调用 @PreDestroy 方法之前,logback 也会自行关闭。

以下来源可能很有用:https ://github.com/spring-projects/spring-framework/issues/24431 。

于 2021-04-03T15:51:39.883 回答
1

我不知道你为什么要Spring照顾这个。除非我误解了您的问题,否则您可以使用容器应用程序生命周期。

尝试编写LifeCycle (jetty)LifeCycleListener (tomcat)并覆盖 inLifeCyle onStartonStop. 并在适当的事件发生LifeCycleListener时在 tomcat 中使用类似的解决方案。

于 2013-05-04T10:36:28.187 回答
1

老问题,但解决方案是创建一个类来处理 PreDestroy :

@Component
public class SetBeanProcessor implements BeanPostProcessor, BeanFactoryAware, DisposableBean {

    private BeanFactory beanFactory;
    private final List<Object> prototypeBeans = new LinkedList<>();

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (beanFactory.isPrototype(beanName)) {
            synchronized (prototypeBeans) {
                prototypeBeans.add(bean);
            }
        }
        return bean;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }


    @Override
    public void destroy() throws Exception {

        // note : if we have several instance of a prototype (each instance is distinct) the method will be called several times ... 
        synchronized (prototypeBeans) {
            for (Object bean : prototypeBeans) {

                if (bean instanceof DisposableBean) {
                    DisposableBean disposable = (DisposableBean)bean;
                    try {
                        disposable.destroy();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            prototypeBeans.clear();
        }
    }

使用此类的类应该以这种方式实现:

@Component
public class myClass implements SomeClassName, DisposableBean {
    ...

    @Override
    public void destroy() throws Exception {
        System.out.println("# myClass: destroy() method called");       
    }
}
于 2020-04-13T17:39:18.337 回答
0

您可以使用方法引用“close”在 Spring 中执行 @PreDestroy 方法:

Runtime.getRuntime().addShutdownHook(new Thread(applicationContext::close));
于 2021-03-13T12:14:03.087 回答