官术网_书友最值得收藏!

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 網站計數器顯示信息

主站蜘蛛池模板: 枣庄市| 江川县| 龙里县| 天峨县| 普洱| 望城县| 读书| 乐亭县| 麻城市| 湖南省| 临潭县| 黄大仙区| 衡山县| 福泉市| 弥渡县| 马关县| 深泽县| 闻喜县| 阿拉善左旗| 宁德市| 肥乡县| 正宁县| 舟山市| 宁夏| 吉林省| 忻州市| 揭阳市| 龙江县| 清水县| 仙桃市| 教育| 犍为县| 天气| 原阳县| 健康| 瑞安市| 弥渡县| 当阳市| 五家渠市| 德保县| 原平市|