web-dev-qa-db-ja.com

WebアプリケーションにshutDownHookを登録する

Webアプリケーションでシャットダウンフックを登録するにはどうすればよいですか?

Web.xmlまたはapplicationContext.xmlに登録する必要がありますか?

メインクラスでアプリケーションを使用している場合、それは簡単です。

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

しかし、Webアプリケーションについてはどうでしょうか? ContextListenerを使用するため

23
slisnychyi

スタンドアロン(非Web)アプリケーションのregisterShutdownHook():

_@PreDestroy_注釈は、Beanメソッドで使用され、Beanがコンテキストから削除されるとき、またはコンテキストがシャットダウンされるときに通知されます。

シャットダウンイベントは、context.close()またはcontext.registerShutdownHook()が呼び出されたときに発生します。

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

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

すでにこれを知っていることを望みます。


WebアプリケーションのregisterShutdownHook():

Webアプリケーションでは、DispatcherServlet/ContextListenerはApplicationContextを作成し、サーバーがシャットダウンするとコンテキストを閉じます。 do n'tcontext.close()またはcontext.registerShutdownHook()を明示的に呼び出す必要があります。

サーバーがシャットダウンすると、Beanの_@PreDestory_メソッドが自動的に通知されます。

21
Kalyan

Webアプリケーションでは、 ServletContextListener を使用できます。これは、アプリケーションがデプロイおよびアンデプロイされたときに起動します。

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

現在の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");
    //...
}
5
Luiggi Mendoza

Spring 3+を使用して、ContextCleanupListenerをアプリケーションコンテキストに追加できます。

起動時にリスナーを次のように登録します(xml configを使用することもできますが、同じことが当てはまります)

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;
    }
} 

シャットダウンコードを実行する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();
    }

}

たとえば、Tomcatを実行し、CTRL + Cを押してシャットダウンすると、ブレークポイントを配置すると、デバッガーでcontextDestroyedメソッドがヒットするのがすぐにわかります。

2
John Deverall

@Luiggi Mendozaの回答は、web.xmlにエントリを追加すると機能し始めます。

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

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)

しかし重要なことは、最初に@PreDestroyがSpringによって呼び出され、次にcontextDestroyedが呼び出され、再び@PreDestroyが非Springスレッドによって呼び出されることです。

したがって、何らかの作業を完了したい場合は、そのときに、他のリソーススレッドを使用可能にする場合は、この@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();
    }   
}

以下は、クラス内で@postConstructフックを取得するもう1つの方法であることを知ってください。

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

もう1つ、@ PreDestroyは、プロトタイプオブジェクトでは使用できないシングルトンオブジェクトに対してのみ呼び出されます。

0