域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。
这三个监听器接口分别是ServletContextAttributeListener, HttpSessionAttributeListener 和ServletRequestAttributeListener,这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。
首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter。最终得出的结论是:listener -> filter -> servlet
同时还存在着这样一种配置节:context-param,它用于向 ServletContext 提供键值对,即应用程序上下文信息。我们的 listener, filter 等在初始化时会用到这些上下文中的信息,那么 context-param 配置节是不是应该写在 listener 配置节前呢?实际上 context-param 配置节可写在任意位置,因此真正的加载顺序为:context-param -> listener -> filter -> servlet
对于某类配置节而言,与它们出现的顺序是有关的。以 filter 为例,web.xml 中当然可以定义多个 filter,与 filter 相关的一个配置节是 filter-mapping,这里一定要注意,对于拥有相同 filter-name 的 filter 和 filter-mapping 配置节而言,filter-mapping 必须出现在 filter 之后,否则当解析到 filter-mapping 时,它所对应的 filter-name 还未定义。web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法的。
servlet 同 filter 类似 ,此处不再赘述。
由此,可以看出,web.xml 的加载顺序是:context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。
实现HttpSessionAttributeListener 的例子
web.xml中配置:
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>com.brand.common.listener.LogoutListener</listener-class>
</listener>
LogoutListener.java 程序代码:
package com.brand.common.listener;
import java.util.Map;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import com.tugou.bean.UserInofrmationBean;
public class LogoutListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
}
@SuppressWarnings("unchecked")
public void sessionDestroyed(HttpSessionEvent event) {
// 在session销毁的时候 把loginUserMap中保存的键值对清除
UserInofrmationBean user = (UserInofrmationBean) event.getSession()
.getAttribute(UserInofrmationBean.USER_FRONT_SESSION);
Map<String, String> loginUserMap = (Map<String, String>) event.getSession().getServletContext().getAttribute(
"loginUserMap");
if(loginUserMap!=null){
loginUserMap.clear();
}
event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);
}
}
2、实现ServletContextAttributeListener的监听例子,读取fdfs.properties配置文件
web.xml配置:
<listener>
<description>初始化资源信息</description>
<listener-class>com.brand.common.listener.InitEnvListener</listener-class>
</listener>
InitEnvListener.java程序代码:
package com.brand.common.listener;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.web.context.WebApplicationContext;
public class InitEnvListener implements ServletContextListener {
@SuppressWarnings("unused")
private WebApplicationContext springContext;
public void contextDestroyed(ServletContextEvent evt) {
}
public void contextInitialized(ServletContextEvent evt) {
try{
FileInputStream in =null;
InputStreamReader inreader=null;
try{
Properties props = new Properties();
File fdfsConfig=new File(Thread.currentThread().getContextClassLoader().getResource("fdfs.properties").getPath());
in= new FileInputStream(fdfsConfig);
inreader=new InputStreamReader(in,"utf-8");
props.load(inreader);
String imagerootpath = props.getProperty("imagerootpath");
evt.getServletContext().setAttribute("fdfs.imagerootpath", imagerootpath);//页面上取时用application
}finally{
if(inreader!=null){
inreader.close();
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}