实现 ServletContextListener 接口来设置应用共享参数/实现启动任务
在Spring项目中,很多初始化的工作都在后台悄悄完成,例如:初始化加载配置信息/数据等进入内存,或者开启后台任务等。具体是怎么实现的呢?
简单而言,都和 ServletContextListener 接口密切相关。它能监听 ServletContext 对象(web应用)的生命周期。当 Servlet 容器启动或者终止web应用时,会触发ServletContextEvent 事件。该事件由 ServletContextListener 处理,在ServletContextListener 接口中定义了处理该事件的两个方法:
//当Servlet容器终止web应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet和Filter。
void contextDestroyed(ServletContextEvent sce)
/* Notification that the servlet context is about to be shut down. All servlets and filters have been destroyed before any ServletContextListeners are notified of context destorytion.*/
//当Servlet容器启动web应用时调用如下方法,调用后容器再初始化filter,并对需要被初始化的Servlet进行初始化。
void contextInitialized(ServletContextEvent sce)
/* Notification that the web application initialization process is starting. All ServletContextListeners are notified of context initialization before any filter or servlet in the web application is initialized.*/
容器 container 的四个子容器:Engine(每个service只能有1个)、Host、Context(代表一个应用程序or一个WEB-INF目录+其内的web.xml文件)、Wrapper。
javax.Servlet 包
包含描述和定义 servlet class 与 运行时环境之间(servlet容器提供给这个class实例)协定的类和接口。(The javax.servlet package contains a number of classes and interfaces that describe and define the contracts between a servlet class and the runtime environment provided for an instance of such a class by a conforming servlet container)
ServletContext 接口
这个接口定义一些 servlet 用来与 它的 servlet 容器通信的方法,例如:
getAttribute(); getContext(); getInitParameter(); getResource(); getServerInfo(); setAttribute(); log(java.lang.String msg)。
(Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file. )
ServletContext 对象
为整个web应用提供共享的内存,任何请求都可以访问里面的内容。是web容器(如tomcat)提供的全局上下文环境,为后面的Spring IOC容器提供宿主环境。
ServletContextListener 接口
这个接口的实现接收所在web应用 servlet context的改变通知
(Implementations of this interface receive notifications about changes to the servlet context of the web application they are part of.)
ServletContextEvent 类
web应用servlet context 改变的通知类
(This is the event class for notifications about changes to the servlet context of a web application.)
使用示例 1 - 服务启动时动态的加入 ServletContext 内容
1、StartInitListener 类实现 servletContextListerner 接口,将要共享的值通过传入的ServletContextEvent 的 getServletContext() 方法得到的 ServletContext,运行ServletContextEvent.setAttribute(key,value) 设置在内存中 ;
2、在项目的 \WEB-INF\web.xml 中配置监听器:
<listener>
<listener-class>com.testCompany.framework.StartInitListener</listener-class>
</listener>
3、项目通过ServletContext sct=getServletConfig().getServletContext(); sct.getAttribute(key) 将数据取到 。
使用示例 2 - 利用 contextDestroyed/contextInitialized 进行应用访问计数
1、方法 contextInitialized 在 Web 应用启动时从文件中读取计数器的数值,并设置在 ServletContext 中;
2、方法 contextDestroyed 在Web应用结束前从 ServletContext 获取计数器的数值,并写入文件 context.getRealPath(“/count”)。
使用示例 3 - 利用 contextDestroyed/contextInitialized + 配置文件生成实例存入 servletContext
public class Bootstrap implements ServletContextListener {
private ServletOAuthClient client;
@Override
public void contextInitialized(ServletContextEvent sce) {
client = new ServletOAuthClient();
ServletContext context = sce.getServletContext();
configureClient(context);
context.setAttribute(ServletOAuthClient.class.getName(), client);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
client.stop();
}
private void configureClient(ServletContext context) {
InputStream is = null;
String path = context.getInitParameter("keycloak.config.file");
if (path == null) {
is = context.getResourceAsStream("/WEB-INF/keycloak.json");
} else {
try {
is = new FileInputStream(path);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
client = ServletOAuthClientBuilder.build(is);
}
}
使用示例 4 - 利用 contextDestroyed/contextInitialized + 配置文件生成实例启动后台线程
private void executeConfig(ServletContextEvent context) {
// Properties pr = new Properties();
OrderProperties pr = new OrderProperties();
pr = PropertiesReader.getOrderProperties("startInit");
Iterator ite = pr.keySet().iterator();
while (ite.hasNext()) {
String clazz = pr.getProperty(ite.next().toString().trim());
int tagIndex = clazz.indexOf(PARAM_TAG);
String tag = "";
if(tagIndex != -1){
tag = clazz.substring(tagIndex+1,clazz.length());
clazz = clazz.substring(0,tagIndex);
}
try {
if(!StringUtils.isEmpty(clazz)){
Class c = Class.forName(clazz.trim());
Object obj = c.newInstance();
if (obj instanceof IStartInit) {
if(tagIndex != -1){
((StartInit) obj).setParam(tag);
}
((IStartInit) obj).execute(context);
} else {
throw new RuntimeException(clazz,e);
}
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RuntimeException(clazz, e);
}
}
}
其它监听器(略)
ServletContextAttributeListener
监听对ServletContext属性的操作,比如增加、删除、修改属性。
HttpSessionListener
监听HttpSession的操作。当创建一个Session时,激发session Created(HttpSessionEvent se)方法;当销毁一个Session时,激发sessionDestroyed (HttpSessionEvent se)方法。
HttpSessionAttributeListener
监听HttpSession中的属性的操作。当在Session增加一个属性时,激发attributeAdded(HttpSessionBindingEvent se) 方法;当在Session删除一个属性时,激发attributeRemoved(HttpSessionBindingEvent se)方法;当在Session属性被重新设置时,激发attributeReplaced(HttpSessionBindingEvent se) 方法。
参考资料
http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletContextListener.html
https://my.oschina.net/bayuanqian/blog/160073
http://www.programcreek.com/java-api-examples/index.php?api=javax.servlet.ServletContextEvent
http://www.cnblogs.com/mfc-itblog/p/5947945.html