java - injection - spring ioc example
Registrar shutDownHook en la aplicaciĆ³n web. (4)
¿Cómo podemos registrar el gancho Shutdown en la aplicación web?
¿Hay algún whays para registrarlo en web.xml o en applicationContext.xml?
Sé que si estamos usando la aplicación con clase principal es simple.
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
context.registerShutdownHook();
Pero ¿qué pasa con la aplicación web? Como utiliza ContextListener
En las aplicaciones web, puede usar un ServletContextListener
que se ServletContextListener
cuando su aplicación se implementa y no se despliega:
public class MyServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
//application is being deployed
}
public void contextDestroyed(ServletContextEvent sce) {
//application is being undeployed
}
}
Puedes acceder a tus Spring beans recuperando el contexto Spring actual:
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");
//...
}
La respuesta de @Luiggi Mendoza comienza a funcionar cuando agrego una entrada en web.xml.
<web-app ...>
<listener>
<listener-class>
com....MyServletContextListener
</listener-class>
</listener>
</web-app>
puede ver el seguimiento de la pila de inicializar / notificar el objeto de escucha por el tomcat; que es mucho antes de que llegue la primavera.
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)
Pero lo importante es que primero se llama @PreDestroy por Spring, luego se llama contextDestroyed y nuevamente se llama a @PreDestroy por un hilo no spring.
Así que si quieres completar algún trabajo; en ese momento, si desea asegurarse de que haya otros hilos de recursos disponibles, mantenga presionado este @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();
}
}
Solo sepan que lo que sigue es una forma más de obtener el enlace @postConstruct dentro de la clase.
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
applicationContext.getBean("myRelatedClass", MyRelatedClass.class);
}
Una cosa más es que se llama a @PreDestroy solo para objetos singleton no disponibles para objetos prototipo.
Usando Spring 3+ puede agregar un ContextCleanupListener al contexto de la aplicación.
Registre a su oyente en el inicio como tal (es posible que prefiera utilizar la configuración xml pero se aplica lo mismo)
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;
}
}
Implementación de ContextCleanupListener que ejecuta su código de apagado:
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();
}
}
Cuando se ejecuta, digamos Tomcat, por ejemplo, y presiona CTRL + C para apagarlo, inmediatamente verá que el método contextDestroyed es golpeado en el depurador si coloca un punto de interrupción allí.
registerShutdownHook () en una aplicación independiente (no web):
La anotación @PreDestroy
se utiliza en el método de bean para recibir una notificación cuando el bean se está eliminando del contexto o cuando el contexto se está cerrando.
El evento de apagado se context.close()
cuando se invoca context.close()
o context.registerShutdownHook()
.
@Component(value="someBean")
public class SomeBean {
@PreDestroy
public void destroy() {
System.out.println("Im inside destroy...");
}
}
Espero que ya sepas esto.
registerShutdownHook () en la aplicación web:
En una aplicación web, DispatcherServlet / ContextListener crea ApplicationContext y cerrará el contexto cuando se apague el servidor. No es necesario invocar explícitamente context.close()
o context.registerShutdownHook()
.
Cuando se apague el servidor, los métodos @PreDestory
en su bean se notificarán automáticamente.