上一篇提到Servlet的一個高級特性Filter之後,我們這邊也要來介紹一下另外一個高級特性,那就是Listener。
前言
上一篇提到Servlet的一個高級特性Filter之後,我們這邊也要來介紹一下另外一個高級特性,那就是Listener。
什麼是Listener
Listener和我們在.Net裡面說的Event是差不多的概念。在Asp .net Webform你可以為一個按鈕註冊一個click事件,那麼當按鈕被按了以後,事件裡面的程式碼就會被執行。
Listener是一樣的概念,他有幾個事件可以去被Listen,而只要條件觸發了,那麼裡面的程式碼就會執行。
Listener提供了一種監控機制,讓我們能夠在某些情況發生的時候,執行我們要的程式碼。
什麼能夠被Listen
基本上來說,有三種存放位置被變動以後可以觸發Listener,它們分別是:request,session和Context。
而有兩種情景會被觸發:
- 當存放位置本身在創建和銷毀的時候觸發
- 當存放位置的內容在創建、銷毀和修改的時候觸發。
Listener的基本結構
基本上,Listener都是Interface,而只要有class實作那個Interface,並且註冊到web.xml裡面就可以。
基本Listener是一種廣泛的,意思是只要符合條件的情況下Listener都會被觸發,不過有兩個Listener比較特別,是只作用於實作的那個Class,而那兩個Listener是不需要註冊在web.xml就可以有效的。
存放位置本身在創建和銷毀的時候觸發
總共三個存放位置,而每一個能夠監聽啟動和銷毀的情況,他們都很相似名稱都是xxxListener,xxx代表每一個存放位置,所以就一起介紹。
ServletRequestListener
一樣兩個方法:
- requestInitialized(ServletRequestEvent ev) - 當request進來的時候會執行
- requestDestroyed(ServletRequestEvent ev) - 當request處理完要銷毀前執行
這邊需要注意的是,假設request包含在伺服器的圖片,那麼每一個圖片都會觸發requestInitialized()。
HttpSessionListener
此Interface有兩個方法:
- sessionCreated(HttpSessionEvent ev) - 當Session被建立起來的時候
- sessionDestroyed(HttpSessionEvent ev) - 當Session time out或者被執行session.invalidate()。基本上就是被銷毀前執行
ServletContextListener
此Interface也是兩個方法:
- contextInitialized(ServletContextEvent ev) - 當Web container啟動或者部署WAR的時候執行
- contextDestroyed(ServletContextEvent ev) - 當Web container要停止的時候
當存放內容在創建、銷毀和修改的時候觸發
一樣,結構非常相似,Interface名稱都是xxxAttributeListener。xxx分別代表ServletRequest、HttpSession和ServletContext。
而它們關注是內容變化,因此個自有三個方法,也都是xxxAdded()、xxxReplaced()和xxxRemoved()。xxx分別是:request、session和context。
只Listen有實作的Class
上面的xxxAttributeListener是針對所有只要有動到那三個儲存空間的動作都會被觸發,不過這邊有兩個特別的Listener只會在有實作的Class觸發。
HttpSessionBindingListener
這個有兩個方法,valueBound(HttpSessionBindingEvent ev)和valueUnbound(HttpSessionBindingEvent ev)。
有實作這個interface的class,只要是這個class的object被放入Session(呼叫valueBound())和從Session銷毀(呼叫valueUnbound())都會呼叫對應的方法。
這邊或許有一個問題是,同樣的功能我不是能夠用HttpSessionAttributeListener的sessionAdded()和sessionRemoved()取得嗎?
使用HttpSessionBindingListener的好處是:
- 不需要做type conversion - 因為你知道進來的一定是這個class type
- 如果只是要監控這一個Class,那麼比較方便
當然壞處就是high coupling。
HttpSessionActivationListener
這個其實我還不是很清楚用途。我只知道在Session裡面的物件如果都是Serializable,那麼就不需要擔心,不然的話就要透過這個Listener在sessionWillPassivate( HttpSessionEvent ev)的時候把它存起來,然後再sessionDidActivate(HttpSessionEvent ev)把它讀出來。
好像主要是為了當Session需要在JVM傳遞的時候用到。
配置Listener
除了那兩個只會影響到有事做Class的Listener之外,其他的Listener都需要在Web.xml裡面註冊。這有個好處,哪天如果不需要了,把註冊的地方從web.xml刪除就沒有作用了。
Listener通常放在Servlet的上面。
註冊格式很簡單:
<listener>
<listener-class>{fully qualified class name}</listener-class>
</listener>
Listener的幾個使用情景
Listener有很多用途,這邊我只舉例幾個常用的。
初始化整個網站使用的參數
還記得我們可以再web.xml利用Context-Param的方式設定預設參數。如果我們網站很常讀取這些內容,可以再Web Container啟動的時候把它們讀進memory裡面,減少之後的IO。
這個時候就會用到ServletContextListener的contextInitialized()來達到。
首先定義一個專門用來裝值的class:
package mine;
public class Config {
public static String ipAddress;
public static String port;
}
然後定義一個ServletContextListener:
...
public class ContextInitListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent ev) {
ServletContext context = ev.getServletContext();
mine.Config.ipAddress = context.getInitParameter("ipAddress");
mine.Config.port = context.getInitParameter("port");
}
...
在註冊到web.xml裡面:
<listener>
<listener-class>listener.ContextInitListener</listener-class>
</listener>
之後在任意頁面都可以取得Config裡面的值。
記錄目前瀏覽人數
我們其實可以透過HttpSessionListener來知道目前瀏覽網站的人數。只要sessionDestroy()表示有人離開,而sessionCreate()表示有人進來。
結語
透過這一篇的介紹,希望對於Listener的使用有比較瞭解。
Filter和Listener的搭配使用讓我們可以達到很多效果,而且這些還是low coupling的方式。假設如果上線了客戶和你說不想要/想要某些功能是需要修改到所有頁面才能做到(例如記錄頁面瀏覽人次),這個時候註冊/反註冊filter或者listener都是非常簡單的,只要修改web.xml裡面就能達到。