1、監聽器的基本知識
1.1、概念
監聽器就是一個實現特定接口的普通java程序(事件監聽器),這個程序專門用於監聽另一個java對象(事件源)的方法調用或屬性改變,當被監聽對象發生上述事件後,監聽器某個方法將立即被執行。Java的事件監聽器是由事件類和監聽接口組成,其中,JAVA中監聽接口是繼承java.util.EventListener的類,事件類繼承java.util.EventObject的類。
1.2、監聽器三要素
在監聽器中涉及到了三個重要的組件,即事件源,事件對象和事件監聽器。當事件源發生某個動作的時候,它會調用事件監聽器的方法,並在調用事件監聽器方法的時候把事件對象傳遞進去。在監聽器中就可以通過事件對象獲取得到事件源,從而對事件源進行操作。
2、Servlet規範中定義的監聽器
在Servlet規範中定義的監聽器有很多,分類的方式也有很多,這裏我將以事件源對象的角度進行分類:
- ServletContext相關監聽器:
- ServletContextListener
ServletContext域對象監聽器 - ServletContextAttributeListener
ServletContext域對象存儲數據監聽器,即監聽其中數據變化的監聽器
- ServletRequest相關監聽器:
- ServletRequestListener
ServletRequest域對象監聽器 - ServletRequestAttributeListener
ServletRequest域對象存儲數據監聽器,即監聽其中數據變化的監聽器
- HttpSession相關監聽器:
- HttpSessionListener
HttpSession域對象監聽器,其中關聯的事件對象是HttpSessionEvent。 - HttpSessionIdListener
HttpSession域對象中ID變化的監聽器。和HttpSessionListener 一樣,關聯的事件對象是HttpSessionEvent。 - HttpSessionActivationListener
HttpSession域對象中的JavaBean對象對鈍化和恢復的監聽器。和HttpSessionListener 、HttpSessionIdListener 一樣,關聯的事件對象是HttpSessionEvent。 - HttpSessionAttributeListener
HttpSession域對象存儲數據監聽器,即監聽其中數據變化的監聽器。其中關聯的事件對象是HttpSessionBindingEvent。 - 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監聽器有點兒類似。區別在於:
- HttpSessionBindingListener是用來監聽HttpSession中的JavaBean對象,而HttpSessionAttributeListener是用來監聽域對象HttpSession的,具體是域對象HttpSession中的屬性變化,即前者的事件源是JavaBean對象,而後者是域對象HttpSession;
- 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來進行。