在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只针对不适用于原型对象的单例对象调用。