全面瞭解Servlet規範中定義的監聽器Listener

1、監聽器的基本知識

1.1、概念

  監聽器就是一個實現特定接口的普通java程序(事件監聽器),這個程序專門用於監聽另一個java對象(事件源)的方法調用或屬性改變,當被監聽對象發生上述事件後,監聽器某個方法將立即被執行。Java的事件監聽器是由事件類和監聽接口組成,其中,JAVA中監聽接口是繼承java.util.EventListener的類,事件類繼承java.util.EventObject的類。

1.2、監聽器三要素

  在監聽器中涉及到了三個重要的組件,即事件源,事件對象和事件監聽器。當事件源發生某個動作的時候,它會調用事件監聽器的方法,並在調用事件監聽器方法的時候把事件對象傳遞進去。在監聽器中就可以通過事件對象獲取得到事件源,從而對事件源進行操作。

2、Servlet規範中定義的監聽器

  在Servlet規範中定義的監聽器有很多,分類的方式也有很多,這裏我將以事件源對象的角度進行分類:

  • ServletContext相關監聽器:
  1. ServletContextListener
    ServletContext域對象監聽器
  2. ServletContextAttributeListener
    ServletContext域對象存儲數據監聽器,即監聽其中數據變化的監聽器
  • ServletRequest相關監聽器:
  1. ServletRequestListener
    ServletRequest域對象監聽器
  2. ServletRequestAttributeListener
    ServletRequest域對象存儲數據監聽器,即監聽其中數據變化的監聽器
  • HttpSession相關監聽器:
  1. HttpSessionListener
    HttpSession域對象監聽器,其中關聯的事件對象是HttpSessionEvent。
  2. HttpSessionIdListener
    HttpSession域對象中ID變化的監聽器。和HttpSessionListener 一樣,關聯的事件對象是HttpSessionEvent。
  3. HttpSessionActivationListener
    HttpSession域對象中的JavaBean對象對鈍化和恢復的監聽器。和HttpSessionListener 、HttpSessionIdListener 一樣,關聯的事件對象是HttpSessionEvent。
  4. HttpSessionAttributeListener
    HttpSession域對象存儲數據監聽器,即監聽其中數據變化的監聽器。其中關聯的事件對象是HttpSessionBindingEvent。
  5. HttpSessionBindingListener
    HttpSession域對象中的JavaBean對象對綁定和解綁的監聽器。和HttpSessionAttributeListener 一樣,關聯的事件對象是HttpSessionBindingEvent。
2.1、ServletContext相關的監聽器
2.1.1、ServletContext的初始化、銷燬監聽器

  ServletContext的初始化、銷燬監聽器由ServletContext、ServletContextListener、ServletContextEvent組成。其中ServletContextEvent對應事件對象,ServletContextListener對應事件監聽器接口,ServletContext對應事件源。

  在ServletContextListener事件監聽器接口中,定義了兩個方法,其中contextInitialized()方法用來監聽ServletContext對象的初始化事件,contextDestroyed()方法用來監聽ServletContext對象的銷燬事件。兩個方法的參數都是ServletContextEvent,即事件對象。

ServletContextListener事件監聽器接口:

package javax.servlet;

import java.util.EventListener;

public interface ServletContextListener extends EventListener {

    public void contextInitialized(ServletContextEvent sce);

    public void contextDestroyed(ServletContextEvent sce);
}

ServletContextEvent事件對象:

package javax.servlet;

public class ServletContextEvent extends java.util.EventObject {

    private static final long serialVersionUID = 1L;


    public ServletContextEvent(ServletContext source) {
        super(source);
    }

    public ServletContext getServletContext() {
        return (ServletContext) super.getSource();
    }
}
2.1.2、ServletContext中屬性變化的監聽器

  ServletContext中屬性變化的監聽器由ServletContext中的對象(可能是任意的JavaBean對象)、ServletContextAttributeListener、ServletContextAttributeEvent組成。其中ServletContextAttributeEvent對應事件對象,ServletContextAttributeListener對應事件監聽器接口,ServletContext中的對象對應事件源。

  在ServletContextAttributeListener事件監聽器接口中,定義了三個方法,其中attributeAdded()方法用來監聽ServletContext對象中新的屬性被新增加時的事件,attributeRemoved()方法用來監聽ServletContext對象中原有的屬性被移除時的事件。attributeReplaced()方法用來監聽ServletContext對象中原有屬性被替換時的事件。這三個方法的參數都是ServletContextAttributeEvent,即事件對象。

ServletContextAttributeListener事件監聽器接口:

package javax.servlet;

import java.util.EventListener;

public interface ServletContextAttributeListener extends EventListener {

    public void attributeAdded(ServletContextAttributeEvent scae);

    public void attributeRemoved(ServletContextAttributeEvent scae);

    public void attributeReplaced(ServletContextAttributeEvent scae);
}

ServletContextAttributeEvent事件對象:

package javax.servlet;

public class ServletContextAttributeEvent extends ServletContextEvent {
    private static final long serialVersionUID = 1L;
    private final String name;
    private final 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;
    }
}
2.2、ServletRequest相關的監聽器

  ServletRequest的監聽器和ServletContext的監聽器類似,也是包括了ServletRequest對象的初始化、銷燬監聽器和ServletRequest對象中屬性變化的監聽器。除了事件源本身不一樣外,其他的用法和ServletContext監聽器基本一樣。

2.2.1、ServletRequest的初始化、銷燬監聽器

  ServletRequest的初始化、銷燬監聽器由ServletRequest、ServletRequestListener、ServletRequestEvent組成。其中ServletRequestEvent對應事件對象,ServletRequestListener對應事件監聽器接口,ServletContext對應事件源。

  在ServletRequestListener事件監聽器接口中,定義了兩個方法,其中requestInitialized()方法用來監聽ServletRequest對象的初始化事件,requestDestroyed()方法用來監聽ServletRequest對象的銷燬事件。兩個方法的參數都是ServletRequestEvent,即事件對象。

  ServletRequestListener、ServletRequestEvent源碼和ServletContext中的類似,略……。

2.2.2、ServletRequest中屬性變化的監聽器

  ServletRequest中屬性變化的監聽器由ServletRequest中的對象(可能是任意的JavaBean對象)、ServletRequestAttributeListener、ServletRequestAttributeEvent組成。其中ServletRequestAttributeEvent對應事件對象,ServletRequestAttributeListener對應事件監聽器接口,ServletRequest中的對象對應事件源。

  在ServletRequestAttributeListener事件監聽器接口中,定義了三個方法,其中attributeAdded()方法用來監聽ServletRequest對象中新的屬性被新增加時的事件,attributeRemoved()方法用來監聽ServletRequest對象中原有的屬性被移除時的事件。attributeReplaced()方法用來監聽ServletRequest對象中原有屬性被替換時的事件。這三個方法的參數都是ServletRequestAttributeEvent,即事件對象。

  ServletRequestAttributeListener、ServletRequestAttributeEvent源碼和ServletContext中的類似,略……。

2.3、HttpSession相關的監聽器

  HttpSession的監聽器除了和上述ServletContext、ServletRequest對象類似的監聽器外,還提供了一些其他的監聽器,比如HttpSessionActivationListener、HttpSessionBindingListener等,而且HttpSession的監聽器在實際的應用開發過程中,使用的也是比較多的。下面我們就分別來了解學習相關的監聽器對象。

2.3.1、HttpSession的初始化、銷燬監聽器

  HttpSession的初始化、銷燬監聽器由HttpSession、HttpSessionListener、HttpSessionEvent組成。其中HttpSessionEvent對應事件對象,HttpSessionListener對應事件監聽器接口,HttpSession對應事件源。

  在HttpSessionListener事件監聽器接口中,定義了兩個方法,其中sessionCreated()方法用來監聽HttpSession對象的初始化事件,sessionDestroyed()方法用來監聽HttpSession對象的銷燬事件。兩個方法的參數都是HttpSessionEvent,即事件對象。

HttpSessionListener事件監聽器接口:

package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionListener extends EventListener {

    public void sessionCreated(HttpSessionEvent se);

    public void sessionDestroyed(HttpSessionEvent se);
}

HttpSessionEvent事件對象:

package javax.servlet.http;

public class HttpSessionEvent extends java.util.EventObject {
    private static final long serialVersionUID = 1L;

    public HttpSessionEvent(HttpSession source) {
        super(source);
    }

    public HttpSession getSession() {
        return (HttpSession) super.getSource();
    }
}
2.3.2、HttpSession域對象的ID變化監聽器

 &mesp;HttpSessionIdListener監聽器和HttpSessionListener的用法基本一樣,其中,事件對象也一樣都是HttpSessionEvent。在HttpSessionIdListener監聽器接口中定義了sessionIdChanged()方法,用來監聽HttpSession域對象中的ID變化。

HttpSessionIdListener事件監聽器接口:

package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionIdListener extends EventListener {

    public void sessionIdChanged(HttpSessionEvent se, String oldSessionId);
}
2.3.3、HttpSession中屬性變化(新增、移除、替換)的監聽器

  HttpSession中屬性變化的監聽器由HttpSession中的對象(可能是任意的JavaBean對象)、HttpSessionAttributeListener、HttpSessionBindingEvent組成。其中HttpSessionBindingEvent對應事件對象,HttpSessionAttributeListener對應事件監聽器接口,HttpSession中的對象對應事件源。

  在HttpSessionAttributeListener事件監聽器接口中,定義了三個方法,其中attributeAdded()方法用來監聽HttpSession對象中新的屬性被新增加時的事件,attributeRemoved()方法用來監聽HttpSession對象中原有的屬性被移除時的事件。attributeReplaced()方法用來監聽HttpSession對象中原有屬性被替換時的事件。這三個方法的參數都是HttpSessionBindingEvent,即事件對象。

  細心的同學已經發現了,在監聽HttpSession域對象屬性變化的監聽對象中,對應的事件對象命名和前面講過的ServletContextAttributeEvent、ServletRequestAttributeEvent命名規則不太一樣了,因爲前面都是*AttributeEvent的格式,而這裏使用了HttpSessionBindingEvent類,這是因爲HttpSession域對象屬性監聽器和HttpSessionBindingListener域對象屬性解綁和綁定監聽器使用了同一個類作爲事件對象,而且遵循了HttpSessionBindingListener域對象屬性解綁和綁定監聽器的命名規則。

HttpSessionAttributeListener事件監聽器接口:

package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionAttributeListener extends EventListener {

    public void attributeAdded(HttpSessionBindingEvent se);

    public void attributeRemoved(HttpSessionBindingEvent se);

    public void attributeReplaced(HttpSessionBindingEvent se);
}

HttpSessionBindingEvent 事件對象:

package javax.servlet.http;

public class HttpSessionBindingEvent extends HttpSessionEvent {

    private static final long serialVersionUID = 1L;

    /* The name to which the object is being bound or unbound */
    private final String name;

    /* The object is being bound or unbound */
    private final Object value;

    public HttpSessionBindingEvent(HttpSession session, String name) {
        super(session);
        this.name = name;
        this.value = null;
    }

    public HttpSessionBindingEvent(HttpSession session, String name,
            Object value) {
        super(session);
        this.name = name;
        this.value = value;
    }

    @Override
    public HttpSession getSession() {
        return super.getSession();
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return this.value;
    }
}

2.3.4、HttpSession中JavaBean對象對綁定和解綁的監聽器

  HttpSessionBindingListener監聽器主要用來對HttpSession中JavaBean對象的綁定和解綁進行監聽。在功能上,和HttpSessionAttributeListener監聽器有點兒類似。區別在於:

  1. HttpSessionBindingListener是用來監聽HttpSession中的JavaBean對象,而HttpSessionAttributeListener是用來監聽域對象HttpSession的,具體是域對象HttpSession中的屬性變化,即前者的事件源是JavaBean對象,而後者是域對象HttpSession;
  2. HttpSessionBindingListener監聽器接口是由JavaBean對象來繼承和實現的,且不需要在web.xml中進行配置,而而HttpSessionAttributeListener監聽器需要專門的監聽器類來繼承和實現,且該監聽器類需要在web.xml中配置(也可以使用註解)才能生效。

  在HttpSessionBindingListener事件監聽器接口中,定義了兩個方法,其中valueBound()方法用來監聽HttpSession中JavaBean對象的綁定事件,即指將JavaBean對象存放在session域對象的事件;valueUnbound()方法用來監聽HttpSession中JavaBean對象解綁的事件,即將JavaBean對象從session域對象中刪除的事件。這兩個方法的參數都是HttpSessionBindingEvent,即事件對象,和HttpSessionAttributeListener監聽器的事件對象使用同一個類表示。

HttpSessionBindingListener事件監聽器接口:

package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionBindingListener extends EventListener {

    public void valueBound(HttpSessionBindingEvent event);

    public void valueUnbound(HttpSessionBindingEvent event);
}
2.3.5、HttpSession中JavaBean對象對鈍化和恢復的監聽器

  HttpSessionActivationListener監聽器主要用來對HttpSession中JavaBean對象的鈍化和恢復進行監聽。在用法上,和HttpSessionBindingListener 監聽器有點兒類似,首先是他們的事件源都是JavaBean對象,所以需要JavaBean對象實現HttpSessionActivationListener接口來實現,其次,這類監聽器也不需要再web.xml中配置才生效。
  JavaBean對象鈍化指的是Tomcat正常關閉時,那麼Session對象中的JavaBean對象會被鈍化,數據會保存到Tomcat的work目錄下;JavaBean對象恢復指的是再次打開Tomcat的時候恢復work目錄下這些數據。

  在HttpSessionActivationListener事件監聽器接口中,定義了兩個方法,其中sessionWillPassivate()方法用來監聽HttpSession中JavaBean對象的鈍化事件,即指Tomcat正常關閉時,將JavaBean對象存放會保存到Tomcat的work目錄下的事件;sessionDidActivate()方法用來監聽HttpSession中JavaBean對象恢復的事件,即指再次打開Tomcat的時候,恢復work目錄下數據的事件。這兩個方法的參數都是HttpSessionEvent,即事件對象,和HttpSessionIdListener、HttpSessionListener監聽器的事件對象使用同一個類表示。

HttpSessionActivationListener 事件監聽器接口:

package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionActivationListener extends EventListener {

    public void sessionWillPassivate(HttpSessionEvent se);

    public void sessionDidActivate(HttpSessionEvent se);
}

3、監聽器使用示例

  在Gitee中,我準備了一個完整的Listener示例的Demo,感興趣的童鞋,可以使用,地址:https://gitee.com/hsh2015/servlet-learning/tree/master/servlet-listener

  在該示例中,我們把這篇文章提到的所有監聽器都準備了示例,因爲好多監聽器的用法都是類似的,所以這裏以Session相關的監聽器爲主來演示。

首先項目結構如下所示:

在這裏插入圖片描述
在這裏插入圖片描述
  其中、context、request、session包分別對應了ServletContext、ServletRequest、HttpSession相關監聽器,其中在session.bean子包下包含的是HttpSession的HttpSessionActivationListener和HttpSessionBindingListener的示例;controller包,存放了用於測試controller方法;WEB-INF/pages目錄下,保存了一個測試使用的頁面。

3.1、MyHttpSessionListener監聽器示例

  首先,我們演示MyHttpSessionListener監聽器,因爲在初始化HttpSession的時候,也可能會觸發其他監聽器,爲了避免其他監聽器干擾,@ServletComponentScan註解的包路徑設置成:com.hsh.listener.session.other,即只掃描該目錄下的監聽器配置(@WebListener註解的類)。

在Application.class類中的配置如下:

//可以掃描到com.hsh.listener包下的監聽器類
@ServletComponentScan(value = { "com.hsh.listener.session.other" })
@ComponentScan
@SpringBootApplication
public class ListenerApplication extends SpringBootServletInitializer {

	public static void main(String[] args) {
		SpringApplication.run(ListenerApplication.class, args);
	}
	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(ListenerApplication.class);
	}
}

在IndexController.class測試類中添加如下方法:

@Controller
public class IndexController {
	
	@RequestMapping("/index")
    public String session(HttpServletRequest request, HttpServletResponse response){
        return "SessionTest";
    }
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response){
		HttpSession session = request.getSession();
        session.invalidate();
        System.out.println("Session對象被銷燬了");
        return "SessionTest";
    }
    //其他方法省略了
}

MyHttpSessionListener 監聽器代碼如下所示:

@WebListener
public class MyHttpSessionListener implements HttpSessionListener, HttpSessionIdListener{

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		System.out.println("MyHttpSessionListener-sessionCreated()方法,HttpSession初始化……");
	}
	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		System.out.println("MyHttpSessionListener-sessionDestroyed()方法,HttpSession銷燬……");
	}
	@Override
	public void sessionIdChanged(HttpSessionEvent se, String oldSessionId) {
		System.out.println("MyHttpSessionListener-sessionIdChanged()方法,HttpSessionID發生變化……");
	}
}

訪問http://localhost:8080/servlet-listener/index,控制檯打印日誌如下:
在這裏插入圖片描述
頁面如下:
在這裏插入圖片描述

點擊“銷燬Session”按鈕,實際上調用了http://localhost:8080/servlet-listener/logout,控制檯打印日誌如下:

銷燬Session後,返回頁面時,又創建了一個Session,所以又除非了一次初始化操作。

在這裏插入圖片描述

3.2、MyHttpSessionAttributeListener監聽器示例

  MyHttpSessionAttributeListener監聽器代碼如下:

@WebListener
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {

	@Override
	public void attributeAdded(HttpSessionBindingEvent se) {
		System.out.println("MyHttpSessionAttributeListener-attributeAdded()方法,HttpSession屬性對象添加……");
	}

	@Override
	public void attributeRemoved(HttpSessionBindingEvent se) {
		System.out.println("MyHttpSessionAttributeListener-attributeRemoved()方法,HttpSession屬性對象移除……");
	}

	@Override
	public void attributeReplaced(HttpSessionBindingEvent se) {
		System.out.println("MyHttpSessionAttributeListener-attributeReplaced()方法,HttpSession屬性對象替換……");
	}

}

在IndexController.class測試類中添加如下方法:

@RequestMapping("/testSessionAttribute")
    public String testSessionAttribute(HttpServletRequest request, HttpServletResponse response){
		HttpSession session = request.getSession();
		System.out.println("########## 添加  ##############");
		session.setAttribute("username", "張三");
		session.setAttribute("age", 15);
		System.out.println("########### 移除  #############");
		session.removeAttribute("username");
		System.out.println("########## 替換  ##############");
		session.setAttribute("age", 16);
        return "SessionTest";
    }

訪問http://localhost:8080/servlet-listener/testSessionAttribute,控制檯打印日誌如下:

因爲第一次請求會觸發MyHttpSessionListener監聽器的初始化方法,所以會打印日誌。

在這裏插入圖片描述

3.3、MyUserOfBinding監聽器示例

  MyUserOfBinding監聽器,監聽的是JavaBean對象。這裏把抽象出了一個MyUser對象,供HttpSessionBindingListener和HttpSessionActivationListener監聽器公用。首先我們看HttpSessionBindingListener監聽器對應的代碼,首先實現了HttpSessionBindingListener接口,同時繼承了MyUser類,代碼如下:

MyUserOfBinding監聽器代碼:

public class MyUserOfBinding extends MyUser implements HttpSessionBindingListener {

	private static final long serialVersionUID = 1L;

	@Override
	public void valueBound(HttpSessionBindingEvent event) {
		System.out.println("MyUserOfBinding-valueBound()方法,JavaBean對象綁定……");
	}

	@Override
	public void valueUnbound(HttpSessionBindingEvent event) {
		System.out.println("MyUserOfBinding-valueUnbound()方法,JavaBean對象解綁……");
	}
}
public class MyUser implements Serializable {
	
	private static final long serialVersionUID = 1L;

	private String uername;
	private String password;
	private String note;
	
	//getter.setter方法省略了
}

在IndexController.class測試類中添加如下方法:

@RequestMapping("/testSessionAttribute")
    public String testSessionAttribute(HttpServletRequest request, HttpServletResponse response){
		HttpSession session = request.getSession();
		System.out.println("########## 添加  ##############");
		session.setAttribute("username", "張三");
		session.setAttribute("age", 15);
		System.out.println("########### 移除  #############");
		session.removeAttribute("username");
		System.out.println("########## 替換  ##############");
		session.setAttribute("age", 16);
        return "SessionTest";
    }

  ListenerApplication類中@ServletComponentScan註解的包路徑設置成:com.hsh.listener.session.bean,即只掃描該目錄下的監聽器配置(@WebListener註解的類),避免其他監聽器干擾,其他和上面提到的ListenerApplication 代碼一樣,不再重複。

訪問http://localhost:8080/servlet-listener/bindTest,控制檯打印日誌如下:
在這裏插入圖片描述

3.4、MyUserOfActivation監聽器示例

  HttpSessionActivationListener監聽器和HttpSessionBindingListener監聽器一樣,都是監聽JavaBean對象的,不過HttpSessionActivationListener監聽的測試,需要配合Tomcat服務器啓動和關閉來進行測試。因爲通過獨立服務器部署的時候,Application類上的註解@ServletComponentScan就會失效,爲了避免其他監聽器干擾,建議註釋這些監聽器的@WebListener註解。需要使用的代碼如下:

MyUserOfActivation代碼如如下:

MyUser 類和前面的代碼完全一樣。

public class MyUserOfActivation extends MyUser implements HttpSessionActivationListener {

	private static final long serialVersionUID = 1L;

	@Override
	public void sessionWillPassivate(HttpSessionEvent se) {
		System.out.println("MyUserOfActivation-sessionWillPassivate()方法,JavaBean對象鈍化……");
	}
	@Override
	public void sessionDidActivate(HttpSessionEvent se) {
		System.out.println("MyUserOfActivation-sessionDidActivate()方法,JavaBean對象激活……");
	}
}

在IndexController.class測試類中添加如下方法:

@RequestMapping("/activationTest")
    public String activationTest(HttpServletRequest request, HttpServletResponse response){
        HttpSession session = request.getSession();
        MyUserOfActivation user = new MyUserOfActivation();
        user.setUername("user1");
        session.setAttribute("user", user);
        
		return "SessionTest";
    }

  演示流程:首先需要使用獨立的Tomcat部署應用,然後訪問http://localhost:8080/servlet-listener/activationTest,設置信息到Session中,然後關閉Tomcat,這個時候會出現JavaBean對象被鈍化的日誌,然後再啓動Tomcat,又會出現JavaBean被激活的日誌,如下所示:

在這裏插入圖片描述
在這裏插入圖片描述

3.5、其他

  其他演示示例請參考Gitee,其中MyServletContextListener監聽器,需要部署到獨立的Tomcat上,通過啓動和停止Tomcat來進行驗證,其他的均可以通過url來進行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章