Listener(監聽器)



Listener 介紹

觀察者設計模式

在介紹 Listener(監聽器)之前,需要先了解觀察者設計模式,因為所有的監聽器都是觀察者設計模式的體現。

那么什么是觀察者設計模式呢?

它是事件驅動的一種體現形式。就好比在做什么事情的時候被人盯着,當做了某件事時,就會觸發事件。

觀察者模式通常由以下三部分組成:

  1. 事件源:觸發事件的對象。

  2. 事件:觸發的動作,里面封裝了事件源。

  3. 監聽器:當事件源觸發事件時,要做的事情。一般是一個接口,由使用者來實現。(此處還涉及一種設計模式的思想:策略模式)

下圖描述了觀察者設計模式組成:

image

Listener 介紹

在程序當中我們可以對以下情況進行監聽:對象的創建銷毀、域對象中屬性的變化、會話相關內容。

Servlet 規范中共計 8 個監聽器,監聽器都是以接口形式提供的,具體功能需要我們自己來完成。


Listener 配置方式

Listender 有兩種配置方法:

  1. 注解方式
    @WebListener

  2. web.xml 配置方式

    <!-- 配置監聽器 -->
    <listener>
        <listener-class>com.listener.ServletContextListenerDemo</listener-class>
    </listener>

    <listener>
        <listener-class>com.listener.ServletContextAttributeListenerDemo</listener-class>
    </listener>

Servlet 規范中的 8 個監聽器

  • 監聽對象的

    1. ServletContextListener
    2. HttpSessionListener
    3. ServletRequestListener
  • 監聽域中屬性變化的

    1. ServletContextAttributeListener
    2. HttpSessionAttributeListener
    3. ServletRequestAttributeListener
  • 會話相關的感知型

    1. HttpSessionBindingListener
    2. HttpSessionActivationListener

監聽對象的監聽器

1)ServletContextListener

用於監聽 ServletContext 對象的創建和銷毀。

核心方法:

返回值 方法名 作用
void contextlnitialized(ServletContextEvent sce) 對象創建時執行該方法
void contextDestroyed(ServletContextEvent sce) 對象銷毀時執行該方法

ServletContextEvent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 ServletContext
  • 直正的事件指的是創建或銷毀 ServletContext 對象的操作

2)HttpSessionListener

用於監聽 HttpSession 對象的創建和銷毀核心方法。

核心方法:

返回值 方法名 作用
void sessionCreated(HttpSessionEventse) 對象創建時執行該方法
void sessionDestroyed(HttpSessionEvent se 對象銷毀時執行該方法

HttpSessionEvent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 HttpSession
  • 真正的事件指的是創建或銷毀 HttpSession 對象的操作

3)ServletRequestListener

用於監聽 ServletRequest 對象的創建和銷毀核心方法。

核心方法:

返回值 方法名 作用
void requestinitialized(ServletRequestEvent sre) 對象創建時執行該方法
void requestDestroyed(ServletRequestEvent sre) 對象銷毀時執行該方法

ServletRequest5vent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 ServletRequest
  • 真正的事件指的是創建或銷毀 ServletRequest 對象的操作

監聽域中屬性變化的監聽器

4)ServletContextAttributeListener

用於監聽 ServletContext 應用域中屬性的變化核心方法。

核心方法:

返回值 方法名 作用
void attributeAdded(ServletContextAttributeEvent scae) 域中添加屬性時執行該方法
void attributeRemoved(ServletContextAttributeEvent scae) 域中移除屬性時執行該方法
void attributeReplaced(ServletContextAttributeEvent scae) 域中替換屬性時執行該方法

ServletContextAttributeEvent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 ServletContext
  • 直正的事件指的是添加、移除、替換應用域中屬性的操作

5)HttpSessionAttributeListener

用於監聽 HttpSession 會話域中屬性的變化。

核心方法:

返回值 方法名 作用
void attributeAdded(HttpSessionBindingEvent se) 域中添加屬性時執行該方法
void attributeRemoved(HttpSessionBindingEvent se) 域中移除屬性時執行該方法
void attributeReplaced(HttpSessionBindingEvent se) 域中替換屬性時執行該方法

HttpSessionBindingEvent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 HttpSession
  • 真正的事件指的是添加、移除、替換會話域中屬性的操作

6)ServletRequestAttributeListener

用於監聽 ServletRequest 請求域中屬性的變化。

核心方法:

返回值 方法名 作用
void attributeAdded(ServletRequestAttributeEvent srae) 域中添加屬性時執行該方法
void attributeRemoved(ServletRequestAttributeEvent srae) 域中移除屬性時執行該方法
void attributeReplaced(ServletRequestAttributeEvent srae) 域中替換屬性時執行該方法

ServletRequestAttributeEvent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 ServletRequest
  • 真正的事件指的是添加、移除、替換請求域中屬性的操作

監聽會話相關的感知型監聽器

注意:監聽會話相關的感知型監聽器,只要定義了即可使用,無需進行配置。

7)HttpSessionBindingListener

用於感知對象和會話域綁定的監聽器。

核心方法:

返回值 方法名 作用
void valueBound(HttpSessionBindingEvent event) 數據添加到會話域中(綁定)時執行該方法
void valueUnbound(HttpSessionBindingEvent event) 數據從會話域中移除(解綁)時執行該方法

HttpSessionBindingEvent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 HttpSession
  • 直正的事件指的是添加、移除會話域中數據的操作

8)HttpSessionActivationListener

用於感知會話域中對象鈍化(序列化)和活化(反序列化)的監聽器。

核心方法:

返回值 方法名 作用
void sessionWillPassivate(HttpSessionEvent se) 會話域中數據鈍化時執行該方法
void sessionDidActivate(HttpSessionEvent se) 會話域中數據活化時執行該方法

HttpSessionEvent 參數:代表事件對象

  • 事件對象中封裝了事件源,也就是 HttpSession
  • 直正的事件指的是會話域中數據鈍化、活化的操作

Listener 使用示例

ServletContextListener 使用示例

1)編寫監聽器:

/**
 * 用於監聽ServletContext對象創建和銷毀的監聽器
 */
@WebListener
public class ServletContextListenerDemo implements ServletContextListener {

    /**
     * 對象創建時,執行此方法
     * @param sce
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("監聽到了對象的創建");
        // 獲取事件源對象
        ServletContext servletContext = sce.getServletContext();
        System.out.println(servletContext);
    }

    /**
     * 對象銷毀時,執行此方法
     * @param sce
     */
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("監聽到了對象的銷毀");
    }
}

2)啟動並停止 web 服務:

image


ServletContextAttributeListener 使用示例

1)編寫監聽器:

/**
 * 監聽域中屬性發生變化的監聽器
 */
public class ServletContextAttributeListenerDemo implements ServletContextAttributeListener {

    /**
     * 域中添加了數據
     * @param scae
     */
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        System.out.println("監聽到域中加入了屬性");
        /**
         * 由於除了我們往域中添加了數據外,應用在加載時還會自動往域中添加一些屬性。
         * 我們可以獲取域中所有名稱的枚舉,從而看到域中都有哪些屬性
         */
        
        //1.獲取事件源對象ServletContext
        ServletContext servletContext = scae.getServletContext();
        //2.獲取域中所有名稱的枚舉
        Enumeration<String> names = servletContext.getAttributeNames();
        //3.遍歷名稱的枚舉
        while(names.hasMoreElements()){
            //4.獲取每個名稱
            String name = names.nextElement();
            //5.獲取值
            Object value = servletContext.getAttribute(name);
            //6.輸出名稱和值
            System.out.println("name is "+name+" and value is "+value);
        }
    }

    /**
     * 域中移除了數據
     * @param scae
     */
    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        System.out.println("監聽到域中移除了屬性");
    }

    /**
     * 域中屬性發生了替換
     * @param scae
     */
    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        System.out.println("監聽到域中屬性發生了替換");
    }
}

同時,我們還需要借助上個示例的 ServletContextListenerDemo 監聽器,往域中存入數據、替換域中的數據以及從域中移除數據,代碼如下:

/**
 * 用於監聽ServletContext對象創建和銷毀的監聽器
 */
public class ServletContextListenerDemo implements ServletContextListener {

    /**
     * 對象創建時,執行此方法
     * @param sce
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("監聽到了對象的創建");
        //1.獲取事件源對象
        ServletContext servletContext = sce.getServletContext();
        //2.往域中加入屬性
        servletContext.setAttribute("servletContext","test");
    }

    /**
     * 對象銷毀時,執行此方法
     * @param sce
     */
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //1.取出事件源對象
        ServletContext servletContext = sce.getServletContext();
        //2.往域中加入屬性,但是名稱仍采用servletContext,此時就是替換
        servletContext.setAttribute("servletContext","demo");
        System.out.println("監聽到了對象的銷毀");
        //3.移除屬性
        servletContext.removeAttribute("servletContext");
    }
}

2)在 web.xml 中配置監聽器:

<!--配置監聽器-->
<listener>
    <listener-class>com.listener.ServletContextListenerDemo</listener-class>
</listener>

<!--配置監聽器-->
<listener>
    <listener-class>com.listener.ServletContextAttributeListenerDemo</listener-class>
</listener>

3)啟動 web 服務:

image


綜合案例

原始案例

優化需求:

  1. 解決亂碼:使用過濾器統一實現請求和響應亂碼問題的解決。
  2. 檢查登錄:使用過濾器統一實現身份認證。
  3. 優化 JSP 頁面:使用 EL 表達式和 JSTL 。

1)亂碼問題過濾器

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// 解決全局亂碼問題
@WebFilter("/*")
public class EncodingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 將請求和響應對象轉換為和HTTP協議相關
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        // 設置編碼格式
        httpServletRequest.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html;charset=UTF-8");

        // 放行
        chain.doFilter(httpServletRequest, httpServletResponse);

    }
}

2)檢查登錄過濾器

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// 檢查登錄態
@WebFilter(value={"/add.jsp", "/queryServlet"})
public class LoginFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 將請求和響應對象轉換為和HTTP協議相關
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        // 判斷會話域對象中的身份數據
        Object username = httpServletRequest.getSession().getAttribute("username");
        if ("".equals(username) || username == null) {
            // 重定向到登錄頁
            httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/login.jsp");
            return;
        }

        // 放行
        chain.doFilter(httpServletRequest, httpServletResponse);
    }
}

3)優化 JSP:使用 EL 表達式和 JSTL

  1. 修改 add.jsp 的虛擬訪問路徑:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>添加</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/addServlet" method="post" autocomplete="off">
        學生姓名:<input type="text" name="username"><br/>
        學生年齡:<input type="number" name="age"><br/>
        學生成績:<input type="number" name="score"><br/>
        <button type="submit">保存</button>
    </form>
</body>
</html>
  1. 修改 index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
    <title>學生管理系統首頁</title>
</head>
<body>
    <%--
        獲取會話域的數據
        如果獲取到了,則顯示添加和查詢功能
        如果獲取不到,則顯示登錄功能
    --%>
    <c:if test="${sessionScope.username eq null}">
        <a href="${pageContext.request.contextPath}/login.jsp">登錄<a/>
    </c:if>

    <c:if test="${sessionScope.username ne null}">
        <a href="${pageContext.request.contextPath}/add.jsp">添加<a/>
        <a href="${pageContext.request.contextPath}/queryServlet">查詢<a/>
    </c:if>

</body>
</html>
  1. 修改 query.jsp
<%@ page import="com.demo.bean.Student" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<html>
<head>
    <title>學生列表頁面</title>
</head>
<body>
  <table width="600px" border="1px">
    <tr>
      <th>學生姓名</th>
      <th>學生年齡</th>
      <th>學生成績</th>
    </tr>
      <c:forEach items="${students}" var="student">
          <tr align="center">
              <td>${student.username}</td>
              <td>${student.age}</td>
              <td>${student.score}</td>
          <tr/>
      </c:forEach>

  </table>
</body>
</html>
  1. 修改 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登錄頁面</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/loginServlet" method="get" autocomplete="off">
        姓名:<input type="text" name="username"><br/>
        密碼:<input type="password" name="password"><br/>
        <button type="submit">登錄</button>
    </form>
</body>
</html>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM