- Java EE 程序設計
- 郝玉龍編著
- 3671字
- 2019-07-01 10:16:07
3.11 Listener
Listener(監聽器)是Servlet 2.4規范以后增加的新特性。Listener用來主動監聽Web容器事件。所謂Web容器事件,是指Web應用上下文創建銷毀、會話對象創建銷毀以及會話屬性信息的增刪改等。通過事件監聽,Listener對象可以在事件發生前、發生后進行一些必要的處理。Listener實現了Web應用的事件驅動,使得Web應用不僅可以被動地處理客戶端發出的請求,而且可以主動對Web容器的變化進行響應,大大提高了Web應用的能力。
為了實現事件監聽功能,Listener必須實現Listener接口,同時,代表Web容器事件的Event類作為參數傳遞到Listener接口,Listener可以通過它來對Web容器事件進行必要的處理。
目前Servlet規范共有7個Listener接口和5個Event類,Event類與Listener之間的關系如表3-3所示。
表3-3 Servlet規范中支持的Listener接口和Event類

注:新的Java EE規范不再支持ServletRequestListener和ServletRequestAttributeListener接口。
1.ServletContextListener和ServletContextEvent
ServletContextEvent用來代表Web應用上下文事件。ServletContextListener用于監聽Web應用上下文事件。當Web應用啟動時ServletContext被創建或當Web應用關閉時ServletContext將要被銷毀,Web容器都將發送ServletContextEvent事件到實現了ServletContextListener接口的對象實例。
實現ServletContextListener接口的實例必須實現以下接口方法:
·void contextInitialized(ServletContextEvent sce)——通知Listener對象,Web應用已經被加載及初始化。
·void contextDestroyed(ServletContextEvent sce)——通知Listener對象,Web應用已經被銷毀。
ServletContextEvent中最常用的方法為:
·ServletContext getServletContext()——取得ServletContext對象。
Listener通常利用此方法來獲取Servlet上下文信息,然后進行相應的處理。
2.ServletContextAttributeListener和ServletContextAttributeEvent
ServletContextAttributeEvent代表Web上下文屬性事件,它包括增加屬性、刪除屬性、修改屬性等。ServletContextAttributeListener用于監聽上述Web上下文屬性事件。
ServletContextAttributeListener接口主要有以下方法:
·void attributeAdded(ServletContextAttributeEvent scab)——當有對象加入Application的范圍,通知Listener對象。
·void attributeRemoved(ServletContextAttributeEvent scab)——若有對象從Application的范圍移除,通知Listener對象。
·void attributeReplaced(ServletContextAttributeEvent scab)——若在Application的范圍中,有對象取代另一個對象時,通知Listener對象。
ServletContextAttributeEvent中常用的方法如下:
·java.lang.String getName()——返回屬性的名稱。
·java.lang.Object getValue()——返回屬性的值。
3.HttpSessionBindingListener和HttpSessionBindingEvent
HttpSessionBindingEvent代表會話綁定事件。當對象加入Session范圍(即調用HttpSession對象的setAttribute方法的時候)或從Session范圍中移除(即調用HttpSession對象的removeAttribute方法的時候或Session Time out的時候)時,都將觸發該事件,此時,Web容器將發送消息給實現了HttpSessionBindingListener接口的對象。
實現了HttpSessionBindingListener接口的對象必須實現以下兩個方法:
·void valueBound(HttpSessionBindingEvent event)——通知Listener,有新的對象加入Session。
·void valueUnbound(HttpSessionBindingEvent event)——通知Listener,有對象從Session中刪除。
4.HttpSessionAttributeListener和HttpSessionBindingEvent
HttpSessionAttributeListener也是用來監聽HttpSessionBindingEvent事件,但是實現HttpSessionAttributeListener接口的對象必須實現以下不同的接口方法:
·attributeAdded(HttpSessionBindingEvent se)——當在Session增加一個屬性時,Web容器調用此方法。
·attributeRemoved(HttpSessionBindingEvent se)——當在Session刪除一個屬性時,Web容器調用此方法。
·attributeReplaced(HttpSessionBindingEvent se)——當在Session屬性被重新設置時,Web容器調用此方法。
說明:HttpSessionAttributeListener和HttpSessionBindingListener的區別是HttpSession-AttributeListener是從會話的角度去觀察,而HttpSessionBindingListener是從對象綁定的角度來觀察。當會話超時或無效時,對象會從會話解除綁定。此時HttpSessionBindingListener會得到通知,而HttpSessionAttributeListener則不會。
5.HttpSessionListener和HttpSessionEvent
HttpSessionEvent代表HttpSession對象的生命周期事件包括HttpSession對象的創建、銷毀等。HttpSessionListener用來對HttpSession對象的生命周期事件進行監聽。
實現HttpSessionListener接口的對象必須實現以下接口方法:
·sessionCreated(HttpSessionEvent se)——當創建一個Session時,Web容器調用此方法。
·sessionDestroyed(HttpSessionEvent se)——當銷毀一個Session時,Web容器調用此方法。
6.HttpSessionActivationListener接口
主要用于服務器集群的情況下,同一個Session轉移至不同的JVM的情形。此時,實現了HttpSessionActivationListener接口的Listener將被觸發。
7.AsyncListener
對Servlet異步處理事件提供監聽,實現AsyncListener接口的對象必須實現以下接口方法:
·onStartAsync(AsyncEvent event)——異步線程開始時,Web容器調用此方法。
·onError(AsyncEvent event)——異步線程出錯時,Web容器調用此方法。
·onTimeout(AsyncEvent event)——異步線程執行超時,Web容器調用此方法。
·onComplete(AsyncEvent event)——異步線程執行完畢時,Web容器調用此方法。
要注冊一個AsyncListener,只需將準備好的AsyncListener對象作為參數傳遞給AsyncContext對象的addListener()方法即可,如下列代碼片段所示:
AsyncContext ctx = req.startAsync(); ctx.addListener(new AsyncListener() { public void onComplete(AsyncEvent asyncEvent) throws IOException { // 做一些清理工作或者其他 } ... });
利用上述七類Listener接口,Web應用實現了對Web容器的會話以及應用上下文層面上的事件的監聽處理。
除HttpSessionBindingListener接口和AsyncListener接口,其他所有關于Listener的配置信息都存儲在Web應用的部署描述文件Web.xml中,Web容器通過此文件中的信息來決定當某個特定事件發生時,將自動創建對應的Listener對象的實例并調用相應的接口方法進行處理。
下面通過創建一個網站計數器來演示如何應用Listener來開發Web應用。網站計數器要求滿足以下功能:
(1)統計應用自部署以來的所有用戶訪問次數。
(2)對于用戶在一次會話中的訪問只記錄一次,以保證數據的真實性。
(3)統計在線用戶數量信息。
由于網站計數器要記錄應用自部署以來的所有用戶訪問次數,因此,必須將用戶訪問信息實現持久化。由于用戶訪問信息比較簡單,因此,可以將信息持久化存儲到外部的資源文件中,而不是數據庫。
利用ServletContextListener控制歷史計數信息的讀取和寫入。當Web應用上下文創建時,將歷史計數信息從外部資源文件讀取到內存。當Web應用上下文關閉時,則將歷史計數信息持久化存儲到外部資源文件中。
利用HttpSessionListener監聽在線用戶數量變化。每創建一個新的會話,則代表產生一次新的用戶訪問。每一個會話銷毀事件,則代表一位用戶離線。這種方式也避免了用戶重復刷新導致的重復計數。
首先在Web應用的文件夾“Web頁”下建立一個名為Count.txt的空文件,用來存儲歷史訪問數據。
下面在Web應用Chapter3中創建一個輔助工具類CounterFile來實現對資源文件的操作。代碼如程序3-35所示。
程序3-35:CounterFile.java
package com.example; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; //用來操作記錄訪問次數的文件 public class CounterFile { private BufferedReader file; //BufferedReader對象,用于讀取文件數據 public CounterFile() { } //ReadFile方法用來讀取文件filePath中的數據,并返回這個數據 public String ReadFile(String filePath) throws FileNotFoundException { String currentRecord = null; //保存文本的變量 //創建新的BufferedReader對象 file = new BufferedReader(new FileReader(filePath)); String returnStr =null; try { //讀取一行數據并保存到currentRecord變量中 currentRecord = file.readLine(); } catch (IOException e) {//錯誤處理 System.out.println("讀取數據錯誤."); } if (currentRecord == null) //如果文件為空 returnStr = "沒有任何記錄"; else {//文件不為空 returnStr =currentRecord; } //返回讀取文件的數據 return returnStr; } //ReadFile方法用來將數據counter+1后寫入到文本文件filePath中 //以實現計數增長的功能 public synchronized void WriteFile(String filePath, String counter) throws FileNotFoundException { int Writestr = 0; Writestr=Integer.parseInt(counter); try { //創建PrintWriter對象,用于寫入數據到文件中 PrintWriter pw = new PrintWriter(new FileOutputStream(filePath)); //用文本格式打印整數Writestr pw.println(Writestr); //清除PrintWriter對象 pw.close(); } catch(IOException e) { //錯誤處理 System.out.println("寫入文件錯誤"+e.getMessage()); } } }
程序說明:程序用來實現對資源文件Count.txt的讀寫操作。其中方法ReadFile(String filePath)負責將信息從資源文件讀取到內存,方法WriteFile(String filePath, String counter)負責將計數信息寫回到資源文件。為了避免寫文件時發生線程安全問題,這里將方法WriteFile用修飾符synchronized加以保護。
下面創建一個ServletContextListener來實現對Web應用創建、銷毀事件的監聽,以便在Web應用創建或銷毀時從資源文件載入或回寫歷史訪問數據。在“項目”視圖中選中Web應用Chapter3,右擊,在彈出的快捷菜單中選擇“新建”→“Web應用程序監聽程序”命令,彈出“New Web應用程序監聽程序”對話框,如圖3-45所示。

圖3-45 “New Web應用程序監聽程序”對話框
在“類名”文本框中輸入輔助工具類名CountListener,在“包”文本框中輸入Servlet類所在的包名com.servlet,在“要實現的接口”列表中選中“上下文監聽程序”,單擊“完成”按鈕,NetBeans自動生成類CountListener的框架源文件。代碼如程序3-36所示。
程序3-36:CounterListener.java
package com.example; import javax.servlet.ServletContextListener; import javax.servlet.ServletContextEvent; public class CounterListener implements ServletContextListener { String path=""; public void contextInitialized(ServletContextEvent evt) { CounterFile f=new CounterFile(); String name=evt.getServletContext().getInitParameter("CounterPath"); path=evt.getServletContext().getRealPath(name); try{ String temp=f.ReadFile(path); System.out.println(temp); //將計數器的值放入應用上下文 evt.getServletContext().setAttribute("Counter", temp); }catch(Exception e){ System.out.println(e.toString()); } } public void contextDestroyed(ServletContextEvent evt) { try{ String current= (String)evt.getServletContext().getAttribute ("Counter"); CounterFile f=new CounterFile(); f.WriteFile(path, current); }catch(Exception e){ System.out.println(e.toString()); } } }
程序說明:程序調用輔助工具類CounterFile來操作資源文件。在contextInitialized(ServletContextEvent evt)方法中將歷史計數信息從外部讀入到內存,在contextDestroyed(ServletContextEvent evt)方法中將歷史計數信息持久化儲存到外部文件。這樣,在程序運行期間,不管應用的訪問次數多么頻繁,所用計數操作只是操作內存中的變量,應用程序的性能將不會因為頻繁的IO操作而下降。
計數器文件的路徑信息是從Web應用的上下文中讀取的,因此在運行程序之前,要在Web應用上下文中添加一個名為CounterPath的上下文參數。打開Web.xml,切換到“常規”視圖,單擊“上下文參數”條目下的“添加”按鈕,彈出“添加上下文參數”對話框,如圖3-46所示。

圖3-46 “添加上下文參數”對話框
在“參數名稱”文本框中輸入CounterPath,在“參數值”中輸入count.txt,單擊“確定”按鈕,Web上下文參數添加完畢。查看web.xml的源代碼,可以看到添加的上下文參數和監聽器CounterListener配置信息如程序3-37中斜體部分所示。
程序3-37:Web.xml(部分)
… <context-param> <param-name>CounterPath</param-name> <param-value>count.txt</param-value> </context-param> … <listener> <description>ServletContextListener</description> <listener-class>com.servlet.CounterListener</listener-class> </listener> …
為了監聽在線用戶數目,還要創建一個實現HttpSessionListener接口的Listener。在“項目”視圖中選中Web應用Chapter 3,右擊,在彈出的快捷菜單中選擇“新建”→“Web應用程序監聽程序”,彈出“New Web應用程序監聽程序”對話框,如圖3-47所示。

圖3-47 “New Web應用程序監聽程序”對話框
在“類名”文本框中輸入類名SessionListener,在“包”文本框中輸入Servlet類所在的包名com.servlet,在“要實現的接口”列表中選中“HTTP會話監聽程序”,單擊“完成”按鈕,Netbeans自動生成類SessionListener的框架源文件。完整源代碼如程序3-38所示。
程序3-38:SessionListener.java
package com.example; import javax.servlet.http.HttpSessionListener; import javax.servlet.http.HttpSessionEvent; @WebListener( ) public class SessionListener implements HttpSessionListener { public void sessionCreated(HttpSessionEvent evt) { // 修改在線人數 String current= (String)evt.getSession().getServletContext(). getAttribute("online"); if(current==null)current="0"; int c=Integer.parseInt(current); c++; current=String.valueOf(c); evt.getSession().getServletContext().setAttribute("online", current); //修改歷史人數 String his= (String)evt.getSession().getServletContext(). getAttribute("Counter"); if(his==null)his="0"; int total=Integer.parseInt(his)+1; his=String.valueOf(total); evt.getSession().getServletContext().setAttribute("Counter", his); } public void sessionDestroyed(HttpSessionEvent evt) { // TODO在此處添加您的代碼: // 修改在線人數 String current= (String)evt.getSession().getServletContext().getAttribute ("online"); if(current==null)current="0"; int c=Integer.parseInt(current); c--; current=String.valueOf(c); evt.getSession().getServletContext().setAttribute("online", current); } }
程序說明:程序通過監聽會話事件來維護在線人數和歷史訪問次數信息。在sessionCreated(HttpSessionEvent evt)方法中,從Web應用上下文中獲取歷史計數信息和在線人數信息,并分別增加1。在sessionDestroyed(HttpSessionEvent evt)方法中,從Web應用上下文中獲取在線人數信息,并減1。
最后創建一個Servlet來顯示在線用戶數量以及歷史用戶數量。代碼如程序3-39所示。
程序3-39:Counter.java
package com.servlet; … public class Counter extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); String dumb=(String)request.getSession().getAttribute("dumb"); //觸發session事件 String history =(String)getServletContext().getAttribute("Counter"); if( history==null) history="0"; String temp =(String)getServletContext().getAttribute("online"); if(temp==null)temp="0"; out.println("<html>"); out.println("<head>"); out.println("<title>計數器</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>當前訪問人數:" + temp + "</h1>"); out.println("<h1>歷史訪問人數:" + history + "</h1>"); out.println("</body>"); out.println("</html>"); out.close(); } … }
程序說明:從Web應用上下文屬性中獲取歷史訪問次數和在線人數信息,然后通過Servlet輸出到頁面。
程序發布成功后,在瀏覽器地址欄輸入http://localhost:8080/Chapter3/Counter,將得到如圖3-48所示的運行頁面。

圖3-48 網站計數器顯示信息
- Qt 5 and OpenCV 4 Computer Vision Projects
- AngularJS Testing Cookbook
- INSTANT OpenCV Starter
- 單片機C語言程序設計實訓100例:基于STC8051+Proteus仿真與實戰
- Vue.js 3.x從入門到精通(視頻教學版)
- TypeScript實戰指南
- 你不知道的JavaScript(中卷)
- Android玩家必備
- Android Wear Projects
- 深入實踐Kotlin元編程
- Getting Started with Python
- Flink技術內幕:架構設計與實現原理
- Python一行流:像專家一樣寫代碼
- 高效使用Greenplum:入門、進階與數據中臺
- Modular Programming with JavaScript