• <nav id="wkkge"><strong id="wkkge"></strong></nav>
  • <menu id="wkkge"></menu>
  • Listener監聽器

    JavaWeb Listener監聽器

     

     

    監聽器就是一個java類,用來起到監聽作用,當java中的其他組件或功能在發生變化時,所作出的提醒或操作。

     

     

    Listener監聽器都能監聽什么

     

    • 監聽域對象的創建與銷毀

     

    • 監聽域對象屬性值創建更換與銷毀

     

     

    監聽器的創建步驟

     

    監聽三個域對象的創建和銷毀

     

    • ServletContextListener

     

    • ServletRequestListener

     

    • HttpSessionListener

     

     

    詳解JavaWeb中的Listener監聽器

     

    1、基本概念

     

    JavaWeb里面的listener是通過觀察者設計模式進行實現的。對于觀察者模式,這里不做過多介紹,大概講一下什么意思。

     

    觀察者模式又叫發布訂閱模式或者監聽器模式。在該模式中有兩個角色:觀察者和被觀察者(通常也叫做主題)。觀察者在主題里面注冊自己感興趣的事件,當這個事件發生時,主題會通過回調接口的方式通知觀察者。

     

    舉個生活中的例子:訂閱報紙。任何一個家庭或個人都可以向報社訂閱報紙。這里報社就是“主題”,家庭就是“觀察者”。比如家庭需要訂閱明天早晨的報紙,這個就是“事件”。到了第二天早晨,報紙生產出來了,這個就是“事件發生”。當事件發生時,送報員將報紙送到家庭的信箱里面,這里的信箱就是“回調接口”。

     

     

    對于JavaWeb里面的監聽器,Servlet規范定義了一些列的Listener接口類,通過接口類的方式將事件暴露給應用程序,應用程序如果想監聽其感興趣的事件,那么不必去直接注冊對應的事件,而是編寫自己的listener實現相應的接口類,并將自己的listener注冊到servlet容器。當程序關心的事件發生時,servlet容器會通知listener,回調listener里面的方法。這里自定義的listener就是觀察者,servlet容器就是主題。

     

     

    2、樣例分析

     

    上面說了,servlet容器是通過Listener接口類將事件暴露給應用程序的。所以我們與其說是注冊事件,不如說是注冊監聽器。對應到編程步驟就是:1.編寫自己的listener,實現特定的Listener接口。2.在web.xml里面注冊自己的listener。這里以最簡單的監聽器接口ServletContextListener舉例:

     

    ①  TestListener.java

     

    public class TestListener implements ServletContextListener {
    public TestListener() {}
    public void contextInitialized(ServletContextEvent sce) {
    System.out.println("ServletContextListener.contextInitialized");
    }
    public void contextDestroyed(ServletContextEvent sce) {
    System.out.println("ServletContextListener.contextDestroyed");
    }
    }
    

     

    ② web.xml

     

    <listener>
    <listener-class>com.nantang.listener.TestListener</listener-class>
    </listener>
    

     

    當容器啟動時會向日志中輸出"ServletContextListener.contextInitialized",當容器關閉時會輸出"ServletContextListener.contextDestroyed"。詳細的解釋后面會進一步分析。

     

    這里需要注意是,如果在IDE(Eclipse、STS等)演示上面的例子,當啟動服務器時,在控制臺可以看到"ServletContextListener.contextInitialized",當關閉服務器時,是看不到"ServletContextListener.contextDestroyed"的。這不是沒有執行contextDestroyed方法,而是IDE實現的不夠完美。要想驗證確實調用了contextDestroyed,可以在contextDestroyed里面寫一段代碼邏輯往文件輸出內容而不要輸出到控制臺。

     

    3、源碼分析

     

    現在我們分析下,servlet規范為我們定義了哪些事件。更準確的說是定義了哪些監聽接口。下面的介紹都是以servlet3.0規范為準。

     

    servlet3.0為我們提供了8個監聽器接口,按照它們的作用域來劃分的話可以分為三類:

     

    ① servlet上下文相關監聽接口,包括:ServletContextListener和ServletContextAttributeListener。

     

    ② http session相關監聽接口,包括:HttpSessionListener、HttpSessionActivationListener、HttpSessionAttributeListener和HttpSessionBindingListener。

     

    ③ servlet request相關監聽接口,包括:ServletRequestListener和ServletRequestAttributeListener。

     

    其實從接口的命名,各位看官應該能猜出其基本功能。下面我們按分類來解釋。

     

    ● servlet上下文相關監聽接口

     

     

    EventListener

     

    EventListener是一個標記接口,所有的事件監聽器都必須繼承這個接口,這就是servlet規范,沒什么好解釋的。

     

    EventObject

     

    和EventListener類似,EventObject是個事件頂級類,所有具體的事件類都必須繼承EventObject。

     

    之前在介紹Servlet的時候,我們解釋過一個web應用對應一個servlet上下文。所以ServletContextListener和ServletContextAttributeListener監聽的事件的生命范圍是貫穿整個web應用的。下面是這兩個接口的類圖層級關系。

     

    public class EventObject implements java.io.Serializable {
    protected transient Object source;
    public EventObject(Object source) {
    if (source == null)
    throw new IllegalArgumentException("null source");
    this.source = source;
    }
    public Object getSource() {
    return source;
    }
    public String toString() {
    return getClass().getName() + "[source=" + source + "]";
    }
    }
    

     

    這個類很簡單,其本質就一個東西:source。通過類名EventObject和屬性名source,就能看出這個類就干了一件事,持有“事件源對象”。

     

    ServletContextEvent

     

    servlet上下文事件,這個事件類就是對EventObject的簡單繼承。構造方法中提供ServletContext實例作為事件源。因為事件源是servlet上下文,所以提供個getServletContext獲取ServletContext實例。

     

    在我們后續講解其他事件類的時候,都是一個模子,每個事件類都提供相應的構造方法,傳入相應的事件源對象,并提供額外的獲取事件源方法。所以EventObject就是個事件源的基類,所有事件子類的本質就干了一件事,確定具體的事件源對象。

     

    所以我們后面講解事件的地方,一帶而過。

     

    ServletContextListener

     

    public interface ServletContextListener extends EventListener {
    public void contextInitialized ( ServletContextEvent sce );
    public void contextDestroyed ( ServletContextEvent sce );
    }
    

     

    servlet上下文監聽器接口,對應著兩個事件:servlet上下文初始化事件和servlet上下文即將關閉事件。

     

    當web應用初始化的時候,servlet容器會構造ServletContextEven實例,并回調contextInitialize方法。

     

    當servlet上下文即將關閉時,一般是關閉服務器之前,servlet容器會構造ServletContextEven實例,并回調contextDestroyed方法。這里需要注意的是,contextDestroyed方法的執行會在所有的servlet和filter執行完destroy方法之后。

     

    所以如果我們想在應用啟動或關閉時需要做些事情的話,就編寫自己的listener實現該接口。

     

    所有的事件監聽器也是一個模子,按照servlet規范定義相應的事件回調接口方法,方法的入參就是相應的事件源實例。所以我們后面講解監聽器的地方也一帶而過。

     

    ServletContextAttributeEvent

     

    public class ServletContextAttributeEvent extends ServletContextEvent { 
    private String name;
    private Object value;
    public ServletContextAttributeEvent(ServletContext source, String name, Object value) {
    super(source);
    this.name = name;
    this.value = value;
    }
    public String getName() {
    return this.name;
    }
    public Object getValue() {
    return this.value; 
    }
    }
    

     

    ServletContextAttributeEvent表示servlet上下文屬性相關事件,一般當屬性發生改變時會觸發該事件。這個類繼承ServletContextEven,事件源也是ServletContext實例。額外提供屬性名和屬性值的獲取方法。

     

    ServletContextAttributeListener

     

    public interface ServletContextAttributeListener extends EventListener {
    public void attributeAdded(ServletContextAttributeEvent scab);
    public void attributeRemoved(ServletContextAttributeEvent scab);
    public void attributeReplaced(ServletContextAttributeEvent scab);
    }
    

     

    當servlet上文屬性發生增、刪、改的時候,servlet容器構造ServletContextAttributeEvent事件對象,分別回調attributeAdded、attributeRemoved、attributeReplaced方法。

     

    這里需要注意的是attributeReplaced方法,當屬性的值被替換的時候回調。這個時候如果調用ServletContextAttributeEvent.getValue()方法返回的是替換之前的屬性值。

     

    ● http session相關監聽接口

     

     

    HttpSessionEvent

     

    public class HttpSessionEvent extends java.util.EventObject {
    public HttpSessionEvent(HttpSession source) {
    super(source);
    }
    public HttpSession getSession () { 
    return (HttpSession) super.getSource();
    }
    }
    

     

    http session相關事件,當session發生變化時會觸發該事件。事件源是HttpSession實例,并提供額外的HttpSession獲取方法。

     

    HttpSessionListener

     

    public interface HttpSessionListener extends EventListener {
    public void sessionCreated ( HttpSessionEvent se );
    public void sessionDestroyed ( HttpSessionEvent se );
    }
    

     

    當session被創建和銷毀的時候,servlet容器構造HttpSessionEvent事件對象,并回調sessionCreated和sessionDestroyed方法。

     

    HttpSessionActivationListener

     

    public interface HttpSessionActivationListener extends EventListener { 
    public void sessionWillPassivate(HttpSessionEvent se); 
    public void sessionDidActivate(HttpSessionEvent se);
    }
    

     

    當session將要鈍化或已被激活時,servlet容器構造HttpSessionEvent事件對象,回調sessionWillPassivate和sessionDidActivate方法。

     

    這里解釋下鈍化和激活:鈍化是指服務器內存不夠了或者session的活動超時時間到了,把最近不活動的session序列化到磁盤。激活是指某個鈍化的session又被訪問了,從磁盤將session反序列化到內存。

     

    這里可以看出要想鈍化和激活,首先session得可序列化和反序列化。同時我們在編程過程中,session盡量用String、Integer等簡單的對象,盡量不要用list、map等集合。

     

    HttpSessionBindingEvent

     

    public class HttpSessionBindingEvent extends HttpSessionEvent {
    private String name;
    private Object value;
    public HttpSessionBindingEvent(HttpSession session, String name) {
    super(session);
    this.name = name;
    }
    public HttpSessionBindingEvent(HttpSession session, String name, Object value) {
    super(session);
    this.name = name;
    this.value = value;
    }
    public HttpSession getSession () { 
    return super.getSession();
    }
    public String getName() {
    return name;
    }
    public Object getValue() {
    return this.value; 
    }
    }
    

     

    http session的屬性相關事件,當session屬性發生變化時會觸發該事件。事件源是HttpSession實例,并提供額外的獲取HttpSession、屬性名、屬性值的方法。

     

    HttpSessionAttributeListener

     

    public interface HttpSessionAttributeListener extends EventListener {
    public void attributeAdded ( HttpSessionBindingEvent se );
    public void attributeRemoved ( HttpSessionBindingEvent se );
    public void attributeReplaced ( HttpSessionBindingEvent se );
    }
    

     

    當session屬性發生增、刪、改的時候,servlet容器構造HttpSessionBindingEvent事件對象,分別回調attributeAdded、attributeRemoved、attributeReplaced方法。

     

    這里需要注意的是attributeReplaced方法,當屬性的值被替換的時候回調。這個時候如果調用ServletContextAttributeEvent.getValue()方法返回的是替換之前的屬性值。

     

    當調用session的invalidate方法或者session失效時,也會回調attributeRemoved方法。

     

    HttpSessionBindingListener

     

    public interface HttpSessionBindingListener extends EventListener {
    public void valueBound(HttpSessionBindingEvent event);
    public void valueUnbound(HttpSessionBindingEvent event);
    }
    

     

    這個監聽器也是監聽session的屬性變化。當session屬性發生增和刪,也就是屬性值綁定和屬性值解綁的時候,servlet容器構造HttpSessionBindingEvent事件對象,分別回調valueBound、valueUnbound方法。

     

    這么一看和HttpSessionAttributeListener沒什么區別,其實不是這樣。兩者有個本質的區別就是事件觸發的條件。

     

    當session的屬性有任何的變化,servlet容器都會通知HttpSessionAttributeListener。但是對于HttpSessionBindingListener,只有當綁定或解綁的屬性值是監聽器的實例時,servlet容器才會通知。舉例來說:

     

    public class TestListener implements HttpSessionBindingListener{
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
    System.out.println("HttpSessionBindingListener.valueBound");
    }
    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
    System.out.println("HttpSessionBindingListener.valueUnbound");
    }
    }
    

     

    我們自定義監聽器TestListener實現HttpSessionBindingListener,下面我們在代碼中設置如下session屬性:

     

    HttpSession session = request.getSession();
    TestListener testListener=new TestListener();
    session.setAttribute("listener", testListener);
    session.removeAttribute("listener");
    

     

    這里session的屬性值是我們的監聽器TestListener實例。所以這段代碼執行時,servlet容器會通知TestListener并回調valueBound和valueUnbound方法。

     

    這里需要注意的是,當調用session的invalidate方法或者session失效時,也會回調valueUnbound方法。

     

    ● servlet request相關監聽接口

     

     

    ServletRequestEvent

     

    public class ServletRequestEvent extends java.util.EventObject { 
    private ServletRequest request;
    public ServletRequestEvent(ServletContext sc, ServletRequest request) {
    super(sc);
    this.request = request;
    }
    public ServletRequest getServletRequest () { 
    return this.request;
    }
    public ServletContext getServletContext () { 
    return (ServletContext) super.getSource();
    }
    }
    

     

    servlet請求的相關事件,當request發生變化時會觸發該事件。事件源是ServletContext實例,并提供額外的獲取ServletContext和ServletRequest方法。

     

    ServletRequestListener

     

    public interface ServletRequestListener extends EventListener {
    public void requestDestroyed ( ServletRequestEvent sre );
    public void requestInitialized ( ServletRequestEvent sre );
    }
    

     

    當請求初始化或者銷毀時,即客戶端請求進入web應用(進入servlet或者第一個filter)或web應用返回響應給客戶端(退出servlet或者第一個filter)。servlet容器構造ServletRequestEvent實例,回調requestInitialized和requestDestroyed方法。

     

    ServletRequestAttributeEvent

     

    public class ServletRequestAttributeEvent extends ServletRequestEvent { 
    private String name;
    private Object value;
    public ServletRequestAttributeEvent(ServletContext sc, ServletRequest request, String name, Object value) {
    super(sc, request);
    this.name = name;
    this.value = value;
    }
    public String getName() {
    return this.name;
    }
    public Object getValue() {
    return this.value; 
    }
    }
    

     

    servlet請求屬性的相關事件,當請求屬性發生變化時會觸發該事件。事件源是ServletContext實例,并提供額外的獲取屬性名和屬性值的方法。

     

    ServletRequestAttributeListener

     

    public interface ServletRequestAttributeListener extends EventListener {
    public void attributeAdded(ServletRequestAttributeEvent srae);
    public void attributeRemoved(ServletRequestAttributeEvent srae);
    public void attributeReplaced(ServletRequestAttributeEvent srae);
    }
    

     

    當請求的屬性發生增、刪、改的時候,servlet容器構造ServletRequestAttributeEvent事件對象,分別回調attributeAdded、attributeRemoved、attributeReplaced方法。

     

    這里需要注意的是attributeReplaced方法,當屬性的值被替換的時候回調。這個時候如果調用ServletRequestAttributeEvent.getValue()方法返回的是替換之前的屬性值。

     

    4、總結

     

    至此,listener講完了。我們可以發現listener和servlet、filter有個共同點,都是由容器進行調度。我們只需要編寫自己的listener去實現我們關心的監聽器接口并注冊,剩下來的工作就是在我們自己的listener里面編寫業務邏輯。

     

    這邊博文介紹的listener是servlet3.0規范制定的。3.1已經增加了一些事件監聽器接口,道理都是類似的,讀者可以自行去了解。

     

    全部教程
  • <nav id="wkkge"><strong id="wkkge"></strong></nav>
  • <menu id="wkkge"></menu>
  • 面对面棋牌游戏