Java監聽器詳解

一.監聽器的定義

是指專門用於對其他對象身上發生的事件或狀態改變進行監聽和相應處理的對象,當被監視的對象發生變化時,立即採取相應的行動。

Web應用當中監聽器是什麼?

在這裏插入圖片描述
web監聽器由servlet規範提供的,它可以監聽客戶端的請求,服務端的操作,監聽的對象包括ServletContext,HttpSession,ServletRequest三個預對象(內置對象),分別對應aplication,session,request。
在這裏插入圖片描述

內置對象

JSP中一共預先定義了9個這樣的內置對象,分別爲:request、response、session、application、out、pagecontext、config、page、exception
內置對象(又叫隱含對象)特點:

  1. 由JSP規範提供,不用編寫者實例化。
  2. 通過Web容器實現和管理
  3. 所有JSP頁面均可使用
  4. 只有在腳本元素的表達式或代碼段中才可使用(<%=使用內置對象%>或<%使用內置對象%>)
request對象

request 對象是 javax.servlet.httpServletRequest類型的對象。 該對象代表了客戶端的請求信息,主要用於接受通過HTTP協議傳送到服務器的數據。(包括頭信息、系統信息、請求方式以及請求參數等)。request對象的作用域爲一次請求。

session對象

session 對象是由服務器自動創建的與用戶請求相關的對象。服務器爲每個用戶都生成一個session對象,用於保存該用戶的信息,跟蹤用戶的操作狀態。session對象內部使用Map類來保存數據,因此保存數據的格式爲 “Key/value”。 session對象的value可以使複雜的對象類型,而不僅僅侷限於字符串類型。

application對象

application 對象可將信息保存在服務器中,直到服務器關閉,否則application對象中保存的信息會在整個應用中都有效。與session對象相比,application對象生命週期更長,類似於系統的“全局變量”。

Web監聽器概念

Servlet規範中定義的一種特殊類
用於監聽ServletContext,HttpSession和ServletRequest等域對象的創建與銷燬事件用於監聽域對象的屬性發生修改的事件可以在事件發生前,發生後做一些必要的處理

二.Web監聽器的用途

1.統計在線人數和在線用戶
2.系統啓動時加載初始化信息
3.統計網站訪問量
4.跟Spring結合

三.第一個Web監聽器

實現步驟

1.創建一個實現監聽器接口的類

在這裏插入圖片描述
在這裏插入圖片描述
代碼如下:

package com.java.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * @author : xiayj
 * @date : 2019/8/3 17:58
 */
public class FirstListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("contextInitialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("contextDestroyed");
    }
}
2.配置web.xml進行註冊

把監聽對象放到web.xml中

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <listener>
        <listener-class>com.java.listener.FirstListener</listener-class>
    </listener>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <login-config>
        <auth-method>BASIC</auth-method>
    </login-config>
</web-app>

啓動tomcat,控制檯輸出如下說明當web應用啓動時會自動創建一個application對象,且只有一個
在這裏插入圖片描述
關閉tomcat,如下表示當前應用銷燬
在這裏插入圖片描述

監聽器的啓動順序

在這裏插入圖片描述

四.監聽器的分類

1.按監聽的對象劃分

1)用於監聽應用程序環境對象(ServletContext)的事件監聽器
2)用於監聽用戶會話對象(HttpSession)的事件監聽器
3)用於監聽請求消息對象(ServletRequest)的事件監聽器

2.按監聽的事件劃分

1)監聽域對象自身的創建和銷燬的事件監聽器

在這裏插入圖片描述

ServletContext的創建與銷燬

ServletContext實現了ServletContextListener用於監聽它的創建與銷燬事件,一個web項目可以定義多個ServletContextListener,但一個web項目只有一個ServletContext對象。
ServletContextListener有兩個事件處理方法,這兩個方法都會傳入ServletContextEvent事件對象,可以獲取ServletContext對象以及一些初始化參數。ServletContextListener主要用途:可以做定時器,加載全局屬性對象,創建全局的數據庫連接,加載一些緩存信息。
在這裏插入圖片描述
web.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <listener>
        <listener-class>com.java.listener.FirstListener</listener-class>
    </listener>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <login-config>
        <auth-method>BASIC</auth-method>
    </login-config>
<!--配置初始化參數-->
    <context-param>
        <param-name>initParam</param-name>
        <param-value>listener</param-value>
    </context-param>
</web-app>

代碼實現:

package com.java.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * @author : xiayj
 * @date : 2019/8/3 17:58
 */
public class FirstListener implements ServletContextListener {
    //ServletContext是上下文對象,當web應用啓動時創建,web應用銷燬時銷燬
    @Override
    public void contextInitialized(ServletContextEvent sce) {
//項目啓動時獲取初始化參數
        String initParam = sce.getServletContext().getInitParameter("initParam");
        System.out.println("contextInitialized : initParam = " + initParam);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
//銷燬主要用於當數據庫連接關閉時釋放一些不必要的資源
        System.out.println("contextDestroyed");
    }
}

項目啓動結果如下,說明當項目啓動時就加載了在web.xml中配置的參數
在這裏插入圖片描述

HttpSession的創建與銷燬

與ServletContext類似,HttpSession實現了HttpSessionListener用於監聽它的創建與銷燬事件,一個web項目可以定義多個HttpSessionListener,但一個web項目只有一個HttpSession對象。HttpSessionEvent事件對象,可以獲取當前被創建的HttpSession對象。HttpSessionListener主要用途:統計在線人數,記錄訪問日誌。
在這裏插入圖片描述
Session創建:用戶打開瀏覽器第一次訪問我們應用時,這次會話web容器會分配一個session,可以在session中保存一些信息
Session銷燬:1.手動點退出,關閉服務器2.關閉瀏覽器一段時間直到session過期3.不關閉瀏覽器,session超時
實現HttpSessionListener:

package com.java.listener;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * @author : xiayj
 * @date : 2019/8/4 16:59
 */
public class MyHttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("sessionCreated");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("sessionDestroyed");
    }
}

web.xml文件加如下配置

<!--配置session超時時間,1表示1分鐘0表示沒有超時限制-->
<session-config>
    <session-timeout>1</session-timeout>
</session-config>

訪問項目
在這裏插入圖片描述
控制檯輸出如下Listener監聽到session創建
在這裏插入圖片描述
等一分鐘後,監聽到session被銷燬
在這裏插入圖片描述

ServletRequest的創建與銷燬

ServletRequest實現了ServletRequestListener用於監聽它的創建與銷燬事件,一個web項目可以定義多個ServletRequestListener,但一個web項目只有一個ServletRequest對象。ServletRequestListener主要用途:讀取request裏的參數,記錄訪問歷史,路徑。
在這裏插入圖片描述
實現ServletRequestListener

package com.java.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

/**
 * @author : xiayj
 * @date : 2019/8/4 17:54
 */
public class MyServletRequestListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        //獲取當前的請求參數
        String name = sre.getServletRequest().getParameter("name");
        System.out.println("requestInitialized");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("requestDestroyed");
    }
}

Web.xml中增加監聽器配置信息

<listener>
    <listener-class>com.java.listener.MyServletRequestListener</listener-class>
</listener>

啓動項目,打開瀏覽器,查看控制檯如下,可知request監聽到了用戶的訪問請求,並在請求結束後銷燬
在這裏插入圖片描述

2)監聽域對象中的屬性的增加和刪除的事件監聽器

在這裏插入圖片描述
代碼實現:
新建三個類

package com.java.listener;

import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;

/**
 * @author : xiayj
 * @date : 2019/8/10 17:18
 */
public class MyServletRequestAttributeListener implements ServletRequestAttributeListener {
    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
//可知是什麼對象添加了什麼屬性,移除了什麼屬性       System.out.println("ServletRequest_attributeAdded:"+srae.getName());
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        System.out.println("ServletRequest_attributeRemoved:"+srae.getName());
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        System.out.println("ServletRequest_attributeReplaced:"+srae.getName());
    }
}
package com.java.listener;

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
 * @author : xiayj
 * @date : 2019/8/10 16:55
 */
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        System.out.println("HttpSession_attributeAdded:"+se.getName());
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        System.out.println("HttpSession_attributeRemoved:"+se.getName());
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        System.out.println("HttpSession_attributeReplaced:"+se.getName());
    }
}
package com.java.listener;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

/**
 * @author : xiayj
 * @date : 2019/8/10 16:52
 */
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext_attributeAdded:"+scae.getName());
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext_attributeRemoved:"+scae.getName());
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext_attributeReplaced:"+scae.getName());
    }
}

Web.xml文件中註冊監聽器

<listener>
    <listener-class>com.java.listener.MyHttpSessionAttributeListener</listener-class>
</listener>
<listener>
    <listener-class>com.java.listener.MyServletContextAttributeListener</listener-class>
</listener>
<listener>
    <listener-class>com.java.listener.MyServletRequestAttributeListener</listener-class>
</listener>

創建新增各個屬性值得jsp界面,爲了方便在每個頁面都加上了初始化,銷燬的按鈕
index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>這是myweb應用</title>
  </head>
  <body>
  你好!這是一個listener
  <button onclick="location.href='<%=request.getContextPath()%>/init.jsp';">Init</button>
  <button onclick="location.href='<%=request.getContextPath()%>/destory.jsp';">Destory</button>
  </body>
</html>

init.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServletPath();
//添加屬性,讓Listener監聽執行attributeAdded方法
request.setAttribute("requestName","requestValue");
request.getSession().setAttribute("sessionName","sessionValue");
request.getSession().getServletContext().setAttribute("contextName","contextValue");
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
這是初始化值得界面
<button onclick="location.href='<%=request.getContextPath()%>/init.jsp';">Init</button>
<button onclick="location.href='<%=request.getContextPath()%>/destory.jsp';">Destory</button>
</body>
</html>

destory.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServletPath();
    //移除屬性,讓Listener監聽執行attributeRemoved方法
    request.removeAttribute("requestName");
    request.getSession().removeAttribute("sessionName");
    request.getSession().getServletContext().removeAttribute("contextName");
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
這是銷燬界面
<button onclick="location.href='<%=request.getContextPath()%>/init.jsp';">Init</button>
<button onclick="location.href='<%=request.getContextPath()%>/destory.jsp';">Destory</button>
</body>
</html>

超時時間改爲0

<!--配置session超時時間,1表示1分鐘0表示沒有超時限制-->
<session-config>
    <session-timeout>0</session-timeout>
</session-config>

打開主頁面
在這裏插入圖片描述
點擊init,控制檯輸出如下
在這裏插入圖片描述
再次點擊init
在這裏插入圖片描述
點擊destory,屬性被移除,request對象因爲已經被銷燬了,域中已沒有requestName屬性,所以沒有移除信息
在這裏插入圖片描述

3)監聽綁定到HttpSession域中的某個對象的狀態的事件監聽器

在這裏插入圖片描述
綁定:通過getSession().setAttribute將對象保存到session對象中
解除綁定:通過getSession().removeAttribute將對象從session對象中移除
鈍化:將session對象持久化到存儲設備上
活化:將session對象從存儲設備上恢復

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
代碼實現:
新建user類

package com.java.entity;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;

/**
 * @author : xiayj
 * @date : 2019/8/17 16:09
 */
/** 想要鈍化或活化必須實現Serializable接口*/
public class User implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable {
    private String username;
    private String password;
    /** 對象綁定執行*/
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("valueBound Name:"+event.getName());
    }
    /** 對象解除綁定執行*/
    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("valueUnbound Name:"+event.getName());
    }
    /** 鈍化*/
    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        System.out.println("sessionWillPassivate"+se.getSource());
    }
    /** 活化*/
    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println("sessionDidActivate"+se.getSource());
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
綁定/解除綁定測試:

在init文件裏增加如下:

//將用戶實例放到session中
request.getSession().setAttribute("currentUser",new com.java.entity.User());

在destory文件裏增加如下:

request.getSession().removeAttribute("currentUser");

啓動項目,打開首頁:
在這裏插入圖片描述
點擊init和destory,控制檯分別輸出如下,表明當對象實現HttpSessionBindingListener接口後,只要對象被session綁定,就會觸發事件,執行valueBound方法,當解除綁定後,執行valueUnBound方法
在這裏插入圖片描述
在這裏插入圖片描述

鈍化/活化測試

啓動tomcat,打開項目,可以看到控制檯已經創建session
在這裏插入圖片描述
點擊init,可以看到session中保存了幾個屬性值
在這裏插入圖片描述
關閉tomcat,控制輸出如下,sessionWillPassivateorg.apache.catalina.session.StandardSessionFacade@1e7c7811 就是session鈍化的輸出,StandardSession是默認的鈍化管理器
在這裏插入圖片描述
鈍化的文件保存位置如下
在這裏插入圖片描述
在首頁新增如下:

<!-- 在首頁獲取用戶對象 正常情況下因爲tomcat已關閉,session中用戶對象應該已被銷燬,
但因爲實現了HttpSessionActivationListener接口,tomcat重新啓動時會活化保存的session文件-->
<%=request.getSession().getAttribute("currentUser")%>
你好!這是一個listener
<button onclick="location.href='<%=request.getContextPath()%>/init.jsp';">Init</button>
<button onclick="location.href='<%=request.getContextPath()%>/destory.jsp';">Destory</button>

重啓tomcat,控制檯輸出如下:
在這裏插入圖片描述
打開首頁,可以看到之前鈍化的user對象被輸出
在這裏插入圖片描述
Ps:如果出現可以活化,但是無法鈍化的問題即重啓tomcat沒有調用 sessionDidActivate()方法,瀏覽器端也獲取不到重啓前存入的對象
這是因爲每次重啓tomcat會將如下目錄刪除並重新生成,之前鈍化的session文件也會被一起刪除
在這裏插入圖片描述
解決方法:在tomcat安裝目錄下conf/context.xml配置文件裏增加如下配置,directory裏是自定義的session文件存儲路徑

<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
        <Store className="org.apache.catalina.session.FileStore" directory="D:\tomcat\session"/>
</Manager>

在Servlet3.0版本以上提供@WebListener註解將一個實現了特定監聽器接口(比如ServletContextListener,HttpSessionListener)的類定義爲監聽器,這樣在web應用中使用監聽器時不需要在web.xml文件中配置監聽器的相關描述信息,感興趣的可以自己去研究一下,用法差不多。

參考文章:Jsp:九大內置對象https://my.oschina.net/u/3805464/blog/1813805
本文章內容是對慕課網課程內容的一個總結,感興趣的同學可以去慕課網學習該課程
地址如下:https://www.imooc.com/learn/271

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