在Web应用程序中注册shutDownHook

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

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

我知道如果我们使用主类的应用程序,那很简单。

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

但是Web应用程序怎么样? 因为它使用ContextListener

独立(非Web)应用程序中的registerShutdownHook():

@PreDestroy注释用于bean方法,以便在从上下文中删除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,它将在服务器关闭时关闭上下文。 您不需要显式调用context.close()context.registerShutdownHook()

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

在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"); //... } 

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

在启动时注册你的监听器就像这样(你可能更喜欢使用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; } } 

运行关闭代码的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方法。

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

    com....MyServletContextListener    

你可以看到tomcat初始化/通知监听器对象的堆栈跟踪; 这在spring之前就已经很久了。

 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非弹簧线程调用。

所以,如果你想完成一些工作; 在那个时候如果你想确保其他资源线程可用,那么保持这个@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钩子的另一种方法。

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

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