前言:以前寫了一篇關於Filter的文章:http://tianweili.github.io/blog/2015/01/26/java-filter/,如今再來一篇Listener的,Filter和Listener在項目中是常常用到的,巧妙的使用能夠達到事半功倍的效果。故把二者的用法總結一下。java
原文連接:http://tianweili.github.io/blog/2015/01/27/java-listener/git
一、Listener的定義與做用github
監聽器Listener就是在application,session,request三個對象建立、銷燬或者往其中添加修改刪除屬性時自動執行代碼的功能組件。web
Listener是Servlet的監聽器,能夠監聽客戶端的請求,服務端的操做等。spring
二、Listener的分類與使用緩存
主要有如下三類:tomcat
1)、ServletContext監聽服務器
ServletContextListener:用於對Servlet整個上下文進行監聽(建立、銷燬)。session
public void contextInitialized(ServletContextEvent sce);//上下文初始化 public void contextDestroyed(ServletContextEvent sce);//上下文銷燬 public ServletContext getServletContext();//ServletContextEvent事件:取得一個ServletContext(application)對象
ServletContextAttributeListener:對Servlet上下文屬性的監聽(增刪改屬性)。app
public void attributeAdded(ServletContextAttributeEvent scab);//增長屬性 public void attributeRemoved(ServletContextAttributeEvent scab);//屬性刪除 public void attributeRepalced(ServletContextAttributeEvent scab);//屬性替換(第二次設置同一屬性) //ServletContextAttributeEvent事件:能取得設置屬性的名稱與內容 public String getName();//獲得屬性名稱 public Object getValue();//取得屬性的值
2)、Session監聽
Session屬於http協議下的內容,接口位於javax.servlet.http.*包下。
HttpSessionListener接口:對Session的總體狀態的監聽。
public void sessionCreated(HttpSessionEvent se);//session建立 public void sessionDestroyed(HttpSessionEvent se);//session銷燬 //HttpSessionEvent事件: public HttpSession getSession();//取得當前操做的session
HttpSessionAttributeListener接口:對session的屬性監聽。
public void attributeAdded(HttpSessionBindingEvent se);//增長屬性 public void attributeRemoved(HttpSessionBindingEvent se);//刪除屬性 public void attributeReplaced(HttpSessionBindingEvent se);//替換屬性 //HttpSessionBindingEvent事件: public String getName();//取得屬性的名稱 public Object getValue();//取得屬性的值 public HttpSession getSession();//取得當前的session
session的銷燬有兩種狀況:
1>、session超時,web.xml配置:
<session-config> <session-timeout>120</session-timeout><!--session120分鐘後超時銷燬--> </session-config>
2>、手工使session失效
public void invalidate();//使session失效方法。session.invalidate();
3)、Request監聽
ServletRequestListener:用於對Request請求進行監聽(建立、銷燬)。
public void requestInitialized(ServletRequestEvent sre);//request初始化 public void requestDestroyed(ServletRequestEvent sre);//request銷燬 //ServletRequestEvent事件: public ServletRequest getServletRequest();//取得一個ServletRequest對象 public ServletContext getServletContext();//取得一個ServletContext(application)對象
ServletRequestAttributeListener:對Request屬性的監聽(增刪改屬性)。
public void attributeAdded(ServletRequestAttributeEvent srae);//增長屬性 public void attributeRemoved(ServletRequestAttributeEvent srae);//屬性刪除 public void attributeReplaced(ServletRequestAttributeEvent srae);//屬性替換(第二次設置同一屬性) //ServletRequestAttributeEvent事件:能取得設置屬性的名稱與內容 public String getName();//獲得屬性名稱 public Object getValue();//取得屬性的值
4)、在web.xml中配置
Listener配置信息必須在Filter和Servlet配置以前,Listener的初始化(ServletContentListener初始化)比Servlet和Filter都優先,而銷燬比Servlet和Filter都慢。
<listener> <listener-class>com.listener.class</listener-class> </listener>
三、Listener應用實例
1)、利用HttpSessionListener統計最多在線用戶人數
import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class HttpSessionListenerImpl implements HttpSessionListener { public void sessionCreated(HttpSessionEvent event) { ServletContext app = event.getSession().getServletContext(); int count = Integer.parseInt(app.getAttribute("onLineCount").toString()); count++; app.setAttribute("onLineCount", count); int maxOnLineCount = Integer.parseInt(app.getAttribute("maxOnLineCount").toString()); if (count > maxOnLineCount) { //記錄最多人數是多少 app.setAttribute("maxOnLineCount", count); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //記錄在那個時刻達到上限 app.setAttribute("date", df.format(new Date())); } } //session註銷、超時時候調用,中止tomcat不會調用 public void sessionDestroyed(HttpSessionEvent event) { ServletContext app = event.getSession().getServletContext(); int count = Integer.parseInt(app.getAttribute("onLineCount").toString()); count--; app.setAttribute("onLineCount", count); } }
2)、spring使用ContextLoaderListener加載ApplicationContext配置信息
ContextLoaderListener的做用就是啓動Web容器時,自動裝配ApplicationContext的配置信息。由於它實現了 ServletContextListener這個接口,在web.xml配置這個監聽器,啓動容器時,就會默認執行它實現的方法。
ContextLoaderListener如何查找ApplicationContext.xml的配置位置以及配置多個xml:若是在 web.xml中不寫任何參數配置信息,默認的路徑是"/WEB-INF/applicationContext.xml",在WEB-INF目錄下建立 的xml文件的名稱必須是applicationContext.xml(在MyEclipse中把xml文件放置在src目錄下)。若是是要自定義文件 名能夠在web.xml里加入contextConfigLocation這個context參數。
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext-*.xml</param-value><!-- 採用的是通配符方式,查找WEB-INF/spring目錄下xml文件。若有多個xml文件,以「,」分隔。 --> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
3)、Spring使用Log4jConfigListener配置Log4j日誌
Spring使用Log4jConfigListener的好處:
1>動態的改變記錄級別和策略,不須要重啓Web應用。
2>把log文件定在 /WEB-INF/logs/ 而不須要寫絕對路徑。由於系統把web目錄的路徑壓入一個叫webapp.root的系統變量。這樣寫log文件路徑時不用寫絕對路徑了。
3>能夠把log4j.properties和其餘properties一塊兒放在/WEB-INF/ ,而不是Class-Path。
4>設置log4jRefreshInterval時間,開一條watchdog線程每隔段時間掃描一下配置文件的變化。
<context-param> <param-name>webAppRootKey</param-name> <param-value>project.root</param-value><!-- 用於定位log文件輸出位置在web應用根目錄下,log4j配置文件中寫輸出位置:log4j.appender.FILE.File=${project.root}/logs/project.log --> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j.properties</param-value><!-- 載入log4j配置文件 --> </context-param> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>60000</param-value><!--Spring刷新Log4j配置文件的間隔60秒,單位爲millisecond--> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
四、Spring使用IntrospectorCleanupListener清理緩存
這個監聽器的做用是在web應用關閉時刷新JDK的JavaBeans的Introspector緩存,以確保Web應用程序的類加載器以及其加載的類正確的釋放資源。
若是JavaBeans的Introspector已被用來分析應用程序類,系統級的Introspector緩存將持有這些類的一個硬引用。所以,這 些類和Web應用程序的類加載器在Web應用程序關閉時將不會被垃圾收集器回收!而IntrospectorCleanupListener則會對其進行 適當的清理,已使其可以被垃圾收集器回收。
惟一可以清理Introspector的方法是刷新整個Introspector緩存,沒有其餘辦法來確切指定應用程序所引用的類。這將刪除全部其餘應用程序在服務器的緩存的Introspector結果。
在使用Spring內部的bean機制時,不須要使用此監聽器,由於Spring本身的introspection results cache將會當即刷新被分析過的JavaBeans Introspector cache,而僅僅會在應用程序本身的ClassLoader裏面持有一個cache。雖然Spring自己不產生泄漏,注意,即便在Spring框架的 類自己駐留在一個「共同」類加載器(如系統的ClassLoader)的狀況下,也仍然應該使用使用 IntrospectorCleanupListener。在這種狀況下,這個IntrospectorCleanupListener將會妥善清理 Spring的introspection cache。
應用程序類,幾乎不須要直接使用JavaBeans Introspector,因此,一般都不是Introspector resource形成內存泄露。相反,許多庫和框架,不清理Introspector,例如: Struts和Quartz。
須要注意的是一個簡單Introspector泄漏將會致使整個Web應用程序的類加載器不會被回收!這樣作的結果,將會是在web應用程序關閉時,該 應用程序全部的靜態類資源(好比:單實例對象)都沒有獲得釋放。而致使內存泄露的根本緣由其實並非這些未被回收的類!
注意:IntrospectorCleanupListener應該註冊爲web.xml中的第一個Listener,在任何其餘Listener以前 註冊,好比在Spring's ContextLoaderListener註冊以前,才能確保IntrospectorCleanupListener在Web應用的生命週期適當時機 生效。
<!-- memory clean --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener>