Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/120.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
@未调用Spring单例bean的PreDestroy方法_Spring - Fatal编程技术网

@未调用Spring单例bean的PreDestroy方法

@未调用Spring单例bean的PreDestroy方法,spring,Spring,我在beans.xml中定义了一个Springbean,如下所示: <context:annotation-config /> [...] <bean id="myBackend" class="mycompany.BackendBean" scope="singleton" /> 当我运行服务器(mvnjetty:run)时,我可以在控制台中看到init方法的输出,从中我得出结论,执行了init方法 当我按下Ctrl-C并且Jetty开始关闭时,我看不到destroy

我在
beans.xml
中定义了一个Springbean,如下所示:

<context:annotation-config />
[...]
<bean id="myBackend" class="mycompany.BackendBean" scope="singleton" />
当我运行服务器(
mvnjetty:run
)时,我可以在控制台中看到
init
方法的输出,从中我得出结论,执行了
init
方法

当我按下
Ctrl-C
并且Jetty开始关闭时,我看不到
destroy
方法的输出


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

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


尝试在
lifecycle
onStart
onStop
中编写and和override。当适当的情况发生时,在tomcat中为
LifeCycleListener
使用类似的解决方案。

要让Spring在应用程序关闭时调用
@PreDestroy
回调方法,必须添加一个关闭钩子并关闭其中的应用程序上下文。您可以使用
Runtime.getRuntime().addShutdownHook(Thread)
将钩子连接到JVM,如果Jetty提供这样的API,则可以将钩子连接到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();
   }});

当您在类中使用
@Scope(“prototype”)
时,即使尝试使用
context.close()关闭,@PreDestroy也无法工作
context.registerShutdownHook()

对于“原型”范围的bean,这里有一点需要注意

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

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

第1.5.2节()

与其他作用域不同,Spring不管理完整的 原型bean的生命周期**:容器实例化, 配置或组装原型对象,并将其交给 给客户端,没有该原型实例的进一步记录

因此,尽管初始化生命周期回调方法是在 所有对象,无论范围如何,对于原型, 配置的销毁生命周期回调不被调用。这个 客户机代码必须清理原型范围的对象并发布 原型bean所拥有的昂贵资源

让Spring容器释放 原型范围的bean,尝试使用自定义bean后处理器,,它 保存对需要清理的bean的引用


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

旧问题,但解决方案是创建一个类来处理预销毁:

@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();
        }
    }

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

Runtime.getRuntime().addShutdownHook(new Thread(applicationContext::close));

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

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


以下来源可能很有用:。

您是否计划在jetty上运行该应用程序?或者你需要在更多的容器中运行它?我使用Jetty只是为了快速测试。在生产环境中,我使用的是ApacheTomcat7。为此,我们提供了
appContext.RegisterShotDownhook()
。谢谢。我应该将关闭钩子注册放在哪里(在哪个方法中)?@Dmitrisarenko最好的地方是初始化spring应用程序上下文的地方。@Vitaly需要详细说明RegisterSuttonHook()是AbstractApplicationContext的方法,而不是ApplicationContext的方法。如果重新部署应用程序,java应用程序不会关闭,因此不会被调用。因此,如果您正在使用spring boot或禁用了重新部署,请使用此选项。如果您这样做,将导致关闭代码和容器之间存在依赖关系。最好避免这种情况,并保持对Spring的单一依赖,这种依赖已经存在(或者最好删除对Spring的任何依赖,并使用javax.inject注释)。
@Component
public class myClass implements SomeClassName, DisposableBean {
    ...

    @Override
    public void destroy() throws Exception {
        System.out.println("# myClass: destroy() method called");       
    }
}
Runtime.getRuntime().addShutdownHook(new Thread(applicationContext::close));