一旦我的spring web应用程序启动了所有bean,我就需要执行某个过程。为此,我创建了一个ApplicationListener<ContextRefreshedEvent>
。
但是,当我运行应用程序时,它会被多次调用(因为我们有不同命名空间的上下文,例如mvc-servlets等),但我需要只调用一次这个特定的侦听器,并且当所有上下文正确初始化时
有没有办法实现我想要做的事情?
我正在使用spring 3.1.0.RELEASE。
答案 0 :(得分:4)
是的,有一种方法,但它可能有点棘手。您正在谈论的子语境可能是DispatcherServlet
的上下文。如果您有多个,那么每个调度程序servlet将获得一个上下文。
Spring将容器委托给容器,因此在初始化方面没有单一的管理点。首先,初始化根应用程序上下文,然后由容器初始化各种servlet。对于每一个,可能会有另一个背景。
幸运的是,servlet规范通过load-on-startup
参数
load-on-startup元素表示应该加载此servlet (在Web应用程序启动时实例化并调用其init())。这些的可选内容 element必须是一个表示顺序的整数 应该加载servlet。如果值 是负整数,或者元素不是 目前,容器可以自由加载servlet 无论什么时候选择。如果值是正数 整数或0,容器必须加载和 在应用程序初始化servlet 部署。容器必须保证 加载标有较低整数的servlet 在用更高整数标记的servlet之前。该 容器可以选择装载的顺序 具有相同加载启动值的servlet。
所以你应该基本做两件事:
load-on-startup
元素,并确保其中一个具有独特的更高编号考虑以下(简化)web.xml
定义
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>anotherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/first/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>anotherServlet</servlet-name>
<url-pattern>/second/*</url-pattern>
</servlet-mapping>
</web-app>
此设置将导致对侦听器的3次调用。在这种情况下,anotherServlet
是您链中的最后一个,因此您可以按如下方式识别:
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context instanceof ConfigurableWebApplicationContext) { // sanity check
final ConfigurableWebApplicationContext ctx =
(ConfigurableWebApplicationContext) event.getApplicationContext();
if ("anotherServlet-servlet".equals(ctx.getNamespace())) {
// Run your initialization business here
}
}
}
如果您有兴趣了解其来源,请查看FrameworkServlet#initServletBean
。
并不是说你现在仍然可以抛出异常,这仍然会阻止应用程序正确部署。
最后,您还可以确保最后处理您的事件,以防有多个侦听器为该特定事件注册。为此,您需要实现Ordered
接口:
public class YourListener implements ApplicationListener<ContextRefreshedEvent>, Ordered {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) { }
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
答案 1 :(得分:0)
您必须更加清楚您的上下文层次结构,但如果您具有根上下文和servlet上下文的典型设置,则在servlet上下文中声明ApplicationListener
bean。
ContextLoaderListener
将刷新根上下文。 servlet上下文将由DispatcherServlet
使用根上下文作为其父上下文进行刷新。完成此次刷新后,您的ApplicationListener
将收到该活动。
答案 2 :(得分:0)
嗯,在这种情况下,我总是在我的Spring组件的公共方法上使用javax.annotation.PostConstruct注释。