1. 程式人生 > >過濾器(Filter)和監聽器(listener)

過濾器(Filter)和監聽器(listener)

一、過濾器

Filter(過濾器)並不是一個標準的Servlet ,它不能處理使用者請求,也不能對客戶端生成響應。通過Filter技術,開發人員可以實現使用者在訪問某個目標資源之前,對訪問的請求和響應進行攔截,從而實現一些特殊的功能。例如實現URL級別的許可權訪問控制、過濾敏感詞彙、壓縮響應資訊等一些高階功能。
這裡寫圖片描述
過濾器在執行過程中任何時候都可以打斷,只要不執chain.doFilter()方法就不會再執行後面的過濾器和請求的內容。因此,要特別注意過濾鏈的執行順序問題。假如在邏輯出現兩次doFilter會導致有些頁面會執行兩次。
這裡寫圖片描述

(一)Filter開發步驟

1、編寫java類實現Filter介面,並重寫其方法

例如:字元編碼的 Filter

package filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
 * Description:
 * 設定編碼格式的filter
 *
@author lee */
public classContentTypeimplementsFilter{ private String charset; private boolean enabled; @Override public void destroy() {} /** * Description: * 設定編碼格式 */ @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws
IOException, ServletException { //設定編碼格式 if(enabled && charset!=null) resp.setCharacterEncoding(charset); //執行doFilter方法,放行,進入目標資源或下一個過濾器 chain.doFilter(req, resp); } @Override public void init(FilterConfig config) throws ServletException { charset = config.getInitParameter("charset"); enabled = "ture".equals(config.getInitParameter("enabled")); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

2、在 web.xml 檔案中使用和元素對過濾器進行註冊,並指定攔截資源。

<?xml version="1.0" encoding="UTF-8"?>
<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"id="WebApp_ID"version="3.1">
  <display-name>web</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

  <!-- 註冊監聽器 -->

  <filter>
    <!-- 監聽器名稱 -->
    <filter-name>charset</filter-name>
    <!-- 監聽器的class全稱,包名+類名 -->
    <filter-class>filter.ContentType</filter-class>
    <!-- 初始化引數 -->
    <init-param>
        <param-name>charset</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <init-param>
        <param-name>enabled</param-name>
        <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>charset</filter-name>
    <!-- 
    設定 filter 所攔截的請求路徑(過濾器關聯的URL樣式)
    值要與瀏覽器輸入的地址相匹配,通過url-pattern查詢指定的資源
    寫法:
    1.完全匹配  要求以/開始,名稱與url一致.
    2.使用萬用字元 *
    1.目錄匹配   以/開始,以*結束.
    2.副檔名匹配 不能以/開始,以*.xxx對束
    (目錄匹配和副檔名匹配不能同時使用,例如 /*.jsp) 
    -->
    <!-- /*意味著攔截所有資源 -->
    <url-pattern>/*</url-pattern>

    <!-- 根據servlet的內部名稱攔截,攔截名內部名稱為Servlet的servlet -->
    <servlet-name>Servlet</servlet-name>

    <!-- 
    指定過濾器所攔截的資源被 Servlet 容器呼叫的方式。使用者可以設定多個<dispatcher> 子元素用來指定 Filter 對資源的多種呼叫方式進行攔截。
    REQUEST:當用戶直接訪問頁面時,Web容器將會呼叫過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那麼該過濾器就不會被呼叫。
    INCLUDE:如果目標資源是通過RequestDispatcher的include()方法訪問時,那麼該過濾器將被呼叫。除此之外,該過濾器不會被呼叫。
    FORWARD:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那麼該過濾器將被呼叫,除此之外,該過濾器不會被呼叫。
    ERROR:如果目標資源是通過宣告式異常處理機制呼叫時,那麼該過濾器將被呼叫。除此之外,過濾器不會被呼叫 
    -->
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

</web-app>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

Filter鏈
多個Filter的訪問先後順序由 配置在前面的Filter 執行要早於配置在後面的Filter決定 。
(關於在<url-pattern> 元素中,的匹配方式可以參考另外一篇部落格Servlet程式設計基礎 中的對映路徑)

這裡也可以通過註解在註冊過濾器,例如

@WebFilter(filterName="過濾器名稱",urlPattern="需要進行過濾的url")
  • 1
  • 1

(二)Filter的生命週期

過濾器是伺服器啟動的時候建立和初始化,當有請求訪問時就會呼叫過濾器的doFilter方法,當關閉伺服器的時候就會呼叫destroy方法銷燬過濾器。

public void init(FilterConfig filterConfig) throws ServletException;
//1、Filter的建立和銷燬由WEB伺服器負責。web應用程式啟動時建立Filter 的例項物件,並呼叫其init方法,讀取web.xml配置,完成物件的初始化功能。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
//2、當客戶請求訪問與過濾器關聯的URL的時候,將先執行doFilter方法。

public void destroy();//銷燬
//3、Filter物件建立後會駐留在記憶體,當web應用移除或伺服器停止時才銷燬。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(三)FilterConfig物件

在web.xml配置檔案可以為filter配置一些初始化引數,當web容器例項化Filter物件,呼叫其init方法時,會把封裝了filter初始化引數的filterConfig物件傳遞進來。因此開發人員在編寫filter時,通過filterConfig物件的方法,初始化引數。
例如在web.xml檔案中,配置filter初始化引數:

<!-- 部分程式碼 -->
<?xml version="1.0" encoding="UTF-8"?>
<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"id="WebApp_ID"version="3.1">
  <display-name>web</display-name>

  <filter>
    <filter-name>charset</filter-name>
    <filter-class>filter.ContentType</filter-class>
    <!-- 初始化引數 -->
    <init-param>
        <param-name>name1</param-name>
        <param-value>value1</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>charset</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

然後,我們就可以在filter中使用FilterConfig獲取這些初始化引數:

//部分程式碼

public classContentTypeimplementsFilter{
    FilterConfig config;
    @Override
    public void destroy() {
        config=null;}

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
        //獲取初始化引數
        Enumeration e = config.getInitParameterNames();
        while(e.hasMoreElement){
            paramName = e.nextElement();
            paramValue = e.getInitParameter(paramName);
        }
    }
    @Override
    public void init(FilterConfig config) throws ServletException {
        this.config = config;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

(四)過濾器案例

1、執行公用業務程式碼
2、無效資料過濾(引入ckeditor客戶端元件)
3、登陸許可權的判斷
這裡寫連結內容





二、監聽器

監聽器就是一個實現特定介面的普通Java程式,這個程式專門用於監聽另一個java物件的方法呼叫或屬性改變,當被監聽物件觸發某些事件後,監聽器某個方法將立即被執行。
在Servlet規範中定義了多種型別的監聽器,它們用於監聽的事件源分別為 ServletContext, HttpSession 和 ServletRequest 這三個域物件。
監聽器劃分為三種類型:

  • 監聽三個域物件建立和銷燬的事件監聽器
  • 監聽域物件中屬性的增加和刪除的事件監聽器
  • 監聽繫結到 HttpSession 域中的某個物件的狀態的事件監聽器。

這裡寫圖片描述


(一)監聽器的分類

1、監聽物件建立/銷燬的監聽器介面

Interface ServletRequestListener
//用於監聽ServletRequest物件的建立和銷燬。
//主要方法有:
void    requestDestroyed(ServletRequestEvent sre)
void    requestInitialized(ServletRequestEvent sre)

Interface HttpSessionListener
//用於監聽HttpSession物件的建立和銷燬
//主要方法有:
void    sessionCreated(HttpSessionEvent se)
void    sessionDestroyed(HttpSessionEvent se)

//用於監聽 ServletContext 物件的建立和銷燬事件。
//主要方法有:
void    contextDestroyed(ServletContextEvent sce)
void    contextInitialized(ServletContextEvent sce)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2、監聽物件屬性的變化

Servlet規範定義了監聽 ServletContext, HttpSession, HttpServletRequest 這三個物件中的屬性變更資訊事件的監聽器。這三個介面中都定義了三個方法來處理被監聽物件中的屬性的增加,刪除和替換的事件,同一個事件在這三個介面中對應的方法名稱完全相同,只是接受的引數型別不同。

Interface ServletRequestAttributeListener
//主要方法有:
void    attributeAdded(ServletRequestAttributeEvent srae)
void    attributeRemoved(ServletRequestAttributeEvent srae)
void    attributeReplaced(ServletRequestAttributeEvent srae)

Interface HttpSessionAttributeListener
//主要方法有:
void    attributeAdded(HttpSessionBindingEvent event)
void    attributeRemoved(HttpSessionBindingEvent event)
void    attributeReplaced(HttpSessionBindingEvent event)

Interface ServletContextAttributeListener
//主要方法有:
void    attributeAdded(ServletContextAttributeEvent event)
void    attributeRemoved(ServletContextAttributeEvent event)
void    attributeReplaced(ServletContextAttributeEvent event) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3、監聽繫結到 HttpSession 域中的某個物件的變化

Servlet 規範中定義了兩個特殊的監聽器介面來幫助 JavaBean 物件瞭解自己在 Session 域中的這些狀態,實現這兩個介面的類不需要 web.xml 檔案中進行註冊。

儲存在 Session 域中的可以有多種狀態:
1、繫結到 Session 域中;從 Session 域中解除繫結;
2、隨 Session 物件持久化到一個儲存裝置中(鈍化);隨 Session 物件從一個儲存裝置中恢復(活化)
Interface HttpSessionBindingListene
//監聽物件繫結/解綁到session上的事件,即是將物件儲存到session當中就會呼叫該物件實現該監聽器類方法
Interface HttpSessionActivationListener
//實現了該介面的 JavaBean 物件可以感知自己被活化和鈍化的事件
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

這裡寫圖片描述

(二)監聽器的的實現步驟

1、實現相應監聽器的類

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
 * Description:
 * @author lee
 */
public classSessionListenerimplementsHttpSessionListener{

    /**
     * Description:
     */
    @Override
    public void sessionCreated(HttpSessionEvent event) {
    }

    /**
     * Description:
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2、web.xml檔案中配置監聽器

開發人員只需在web.xml檔案中使用標籤配置好監聽器,web容器就會自動把監聽器註冊到事件源中。一個 web.xml 檔案中可以配置多個事件監聽器,web 伺服器按照它們在 web.xml 檔案中的註冊順序來載入和註冊這些 Serlvet 事件監聽器。(這裡不包括不用註冊的兩個監聽器。)

<!-- web.xml檔案部分 -->
<listener>
    <!-- 這裡就是把listener包下的Listener類註冊成為監聽器 -->
    <lisntener-class>listener.Listener</listener-class>
</listener>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

在Servlet3.0下,也可以通過註解@WebListener就可以實現監聽器的註冊,不需要在web.xml中進行配置。

(三)監聽器案例

1、統計線上人數





三、過濾器和監聽器的例項

(一)例項描述

一個網頁,儲存有員工資訊。可以進行管理員的登陸,管理員可以瀏覽、修改員工資訊。管理員可以看得到其他管理員的是否線上。實現步驟:

1、建立資料庫

管理員:編號、名稱、密碼
員工:編號、名稱

2、編寫實體類

用於封裝管理員和員工的資訊
Admin.java、Employee.java

3、資料訪問層

實現對資料的處理
IAdminDao.java 介面、IEmployeeDao.java 介面、AdminDao.java、EmployeeDao.java

4、資料庫連線

實現連線資料庫,獲取資料。
JdbcUtils

5、Servlet

LoginServlet.java 登陸處理
IndexServlet.java 首頁列表查詢Servlet
LogoutServlet.java 退出處理

6、Jsp頁面

Login.jsp
登陸成功, 提交到登陸Servlet處理其業務,跳轉到員工列表
登陸失敗,跳轉到登陸!
List.jsp 退出功能,跳轉到登陸頁面

7、過濾器

只有登陸後,才可以訪問員工列表。如果沒有登陸,直接訪問首頁列表,要跳轉到登陸!
LoginFilter.java 登陸驗證過濾器
ContentType.java 編碼過濾

8、監聽器

監聽servletContext物件的建立,儲存線上管理員。登陸功能:使用者登陸時候,把資料儲存到servletContext中。退出功能;監聽session銷燬,把當前登陸使用者從onlineuserlist移除!
ContextListener.java 監聽整個的專案的啟動,啟動時就建立線上管理員列表。
SessionListener.java 監聽管理員的登陸


描述圖:
這裡寫圖片描述

程式結構圖:
這裡寫圖片描述
這裡寫圖片描述

注:這裡使用了MySQL資料庫,引用C3P0連線池、dbutil、jstl標籤,以上這些都需要匯入jar包。這裡在這個網站中找到對應的jar包

(二)資料庫設計

1、管理員資料庫
這裡寫圖片描述
2、員工資料庫
這裡寫圖片描述


(三)實體類

1、封裝管理員資訊的實體類

package entity;

/**
 * Description:
 * 一個用來封裝管理員資訊的實體類
 * 
 * @author lee
 * */
public classAdmin {
    private int id;
    private String adName;
    private String password;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public String getAdName() {
        return adName;
    }
    public void setAdName(String adName) {
        this.adName = adName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

2、封裝員工資訊的實體類

package entity;

/**
 * Description:
 * 封裝則員工資訊的實體類
 * 
 * @author lee
 *
 */
public classEmployee {
    private int id;
    private String empName;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName;
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

(四)資料庫連線

package utils;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
 * Description:
 * 這裡使用C3P0的JDBC連線池,來實現資料來源和JNDI(標準的Java命名系統介面)繫結
 * 實現連線資料庫,獲取資料。
 * 
 * @author lee
 */
public classJdbcUtils {

    /**
     * C3P0連線池的核心工具類
     */
    private static DataSource dataSource;
    //靜態初始化連線池
    //C3P0連線池會自動載入src目錄下的c3p0-config.xml配置檔案,來連線mysql資料庫
    static{
        dataSource = new ComboPooledDataSource();
    }

    /**
     * Description:
     * 初始化DbUtils核心工具類物件,並返回
     * 
     * @return 返回DbUtils核心工具類物件
     */
    @Test 
    public static QueryRunner getQueryRunner(){
        //返回建立的QueryRuuner物件,並傳入連線池物件
        return new QueryRunner(dataSource);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14