Java 在 Web 应用程序中注册 shutdownHook

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23936162/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 09:13:59  来源:igfitidea点击:

Register shutDownHook in web application

javaspringtomcatwebioc-container

提问by slisnychyi

How we can registerShutdown hook in web application?

我们如何在 Web 应用程序中注册关闭钩子?

Is there any whays to register it in web.xml or in applicationContext.xml?

有什么办法可以在 web.xml 或 applicationContext.xml 中注册它?

I know that if we are using application with main class it's simple.

我知道如果我们在主类中使用应用程序,那就很简单了。

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    context.registerShutdownHook();

But what about web application? As it uses ContextListener

但是 Web 应用程序呢?因为它使用 ContextListener

采纳答案by Kalyan

registerShutdownHook() in standalone (non-web) application:

registerShutdownHook() 在独立(非网络)应用程序中:

The @PreDestroyannotation is used on bean method to be notified when the bean is being removed from the context or when the context is shutting down.

@PreDestroy上豆方法用于注释当豆被从上下文移除或当所述上下文被关闭时得到通知。

Shut down event is fired when context.close()or context.registerShutdownHook()is invoked.

关闭事件在context.close()context.registerShutdownHook()被调用时被触发。

@Component(value="someBean")
public class SomeBean {

    @PreDestroy
    public void destroy() {
        System.out.println("Im inside destroy...");
    }
}

I hope you already know this.

我希望你已经知道这一点。



registerShutdownHook() in web application:

Web 应用程序中的 registerShutdownHook():

In a web application, DispatcherServlet/ContextListener creates the ApplicationContext and it will close the context when the server shutdown. You don'tneed to explicitly invoke context.close()or context.registerShutdownHook().

在 Web 应用程序中, DispatcherServlet/ContextListener 创建 ApplicationContext 并在服务器关闭时关闭上下文。你并不需要显式调用context.close()context.registerShutdownHook()

When the server shutdown, @PreDestorymethods on your bean will be notified automatically.

当服务器关闭时,@PreDestorybean 上的方法将自动收到通知。

回答by Luiggi Mendoza

In web applications, you can use a ServletContextListenerwhich fires when your application is deployed and undeployed:

在 Web 应用程序中,您可以使用ServletContextListener在部署和取消部署应用程序时触发的a :

public class MyServletContextListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent sce) {
        //application is being deployed
    }
    public void contextDestroyed(ServletContextEvent sce) {
        //application is being undeployed
    }
}

You can access to your Spring beans by retrieving the current Spring context:

您可以通过检索当前 Spring 上下文来访问您的 Spring bean:

public void contextDestroyed(ServletContextEvent sce) {
    ServletContext ctx = sce.getServletContext();
    WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(ctx);
    //retrieve your Spring beans here...
    SomeSpringBean bean = (SomeSpringBean)ctx.getBean("someSprinbgBean");
    //...
}

回答by John Deverall

Using Spring 3+ you can add a ContextCleanupListener to the application context.

使用 Spring 3+,您可以将 ContextCleanupListener 添加到应用程序上下文。

Register your listener at startup like so (you might prefer to use xml config but the same applies)

像这样在启动时注册您的侦听器(您可能更喜欢使用 xml 配置,但同样适用)

package com.myapp

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextCleanupListener;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext)
            throws ServletException {
        WebApplicationContext appContext = getContext();


        servletContext.addListener(new ContextLoaderListener(appContext));

        // line adding an implementation of ContextCleanupListener
        servletContext.addListener(new MyWebApplicationCleanupListener());

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(appContext));
            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
    }

    private AnnotationConfigWebApplicationContext getContext() { 
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation("com.myapp");
        return context;
    }
} 

Implementation of ContextCleanupListener that runs your shutdown code:

运行关闭代码的 ContextCleanupListener 的实现:

package com.myapp;

import javax.servlet.ServletContextEvent;
import com.myapp.resources.requiring.clean.shutdown
import org.springframework.web.context.ContextCleanupListener;

public class MyWebApplicationCleanupListener extends ContextCleanupListener {

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // put your shutdown code in here

        MyResourceNeedingShutdown dataStore = MyResourceNeedingShutdown.getInstance();
        dataStore.shutdown();
    }

}

When you run up say tomcat for example, and press CTRL + C to shut it down, you'll immediately see the contextDestroyed method be hit in the debugger if you put a breakpoint there.

例如,当您运行 tomcat 并按 CTRL + C 将其关闭时,如果在那里放置断点,您将立即在调试器中看到 contextDestroyed 方法。

回答by Kanagavelu Sugumar

@Luiggi Mendoza answer is started working when i add entry in web.xml.

当我在 web.xml 中添加条目时,@Luiggi Mendoza 答案开始工作。

<web-app ...>
   <listener>
    <listener-class>
             com....MyServletContextListener
        </listener-class>
   </listener>
</web-app>

you can see the stack trace of initialising/notifying the listener object by the tomcat; which is much before spring does.

您可以看到tomcat初始化/通知侦听器对象的堆栈跟踪;这比春天早得多。

com....MyServletContextListener.init(nothing but calling @PostConstruct)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.core.DefaultInstanceManager.postConstruct(DefaultInstanceManager.java:203)
    at org.apache.catalina.core.DefaultInstanceManager.postConstruct(DefaultInstanceManager.java:188)
    at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:143)
    at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:119)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4649)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5189)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:724)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:700)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:952)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1823)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

But important thing is @PreDestroy called first by Spring, then after contextDestroyed is called and again @PreDestroy called by non-spring thread.

但重要的是@PreDestroy 首先由Spring 调用,然后在contextDestroyed 被调用之后,@PreDestroy 再次被非Spring 线程调用。

So if you want to complete some work; on that time if you want to ensure other resource thread are available then hold this @PreDestroy.

所以如果你想完成一些工作;那时,如果您想确保其他资源线程可用,请保留此@PreDestroy。

@PreDestroy
public void cleanup() {
    eventTaskExecutor.shutdown();
    try {
        /**
         * This is blocking call to avoid other threads (like logger demon thread)
         * not closed before this completes the job. Else worker thread cannot log
         * event.
         * 
         * This will be the case when thread is busy in getting the web response,
         * better will wait for that, and log the web response.
         * 
         */
        eventTaskExecutor.awaitTermination(20, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }   
}

Just know that the below is one more way of getting the @postConstruct hook within the class.

只要知道下面是在类中获取 @postConstruct 钩子的另一种方法。

@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
    applicationContext.getBean("myRelatedClass", MyRelatedClass.class);
}

One more thing is @PreDestroy is called only for singleton objects not available for prototype objects.

还有一件事是@PreDestroy 仅针对原型对象不可用的单例对象调用。