十三、Filter(過濾器)&Listener(監聽器)

一、Filter:過濾器

1.1、概念

生活中的過濾器:淨水器,空氣淨化器
web中的過濾器:當訪問服務器的資源時,過濾器可以將請求攔截下來,完成一些特殊的功能。
過濾器的作用:
一般用於完成通用的操作。如:登錄驗證、統一編碼處理、敏感字符過濾…

1.2、快速入門

步驟
定義一個類,實現接口Filter
複寫方法

配置攔截路徑
註解(常用)
web.xml

代碼:

@WebFilter("/*") //訪問所有資源之前,都會執行該過濾器
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter被執行了....");


//放行
filterChain.doFilter(servletRequest,servletResponse);

}

@Override
public void destroy() {

}
}

web.xml配置

<  filter>
<  filter-name>demo1</ filter-name>
<  filter-class>cn.itcast.web.filter.FilterDemo1< /filter-class>
< /filter>
< filter-mapping>
< filter-name>demo1< /filter-name>
<!-- 攔截路徑 -->
< url-pattern>/*< /url-pattern>
< /filter-mapping>

1.3、過濾器細節

過濾器執行流程
執行過濾器
執行放行後的資源
回來執行過濾器放行代碼下邊的代碼

過濾器生命週期方法
init: 在服務器啓動後,會創建Filter對象,然後調用init方法。只執行一次。用於加載資源
doFilter: 每一次請求被攔截資源時,會執行。執行多次
destroy: 在服務器關閉後,Filter對象被銷燬。如果服務器是正常關閉,則會執行destroy方法。只執行一次。用於釋放資源

過濾器配置詳解
攔截路徑配置:
具體資源路徑: /index.jsp 只有訪問index.jsp資源時,過濾器纔會被執行
攔截目錄: /user/* 訪問/user下的所有資源時,過濾器都會被執行
後綴名攔截: .jsp 訪問所有後綴名爲jsp資源時,過濾器都會被執行
攔截所有資源:/
訪問所有資源時,過濾器都會被執行

攔截方式配置:資源被訪問的方式
註解配置:
設置dispatcherTypes屬性
REQUEST:默認值。瀏覽器直接請求資源

//瀏覽器直接請求資源時  該過濾器會被執行
@WebFilter(value="/*",dispatcherTypes =DispatcherType.REQUEST )

FORWARD:轉發訪問資源

//瀏覽器轉發訪問請求資源時  該過濾器會被執行
@WebFilter(value="/*",dispatcherTypes =DispatcherType.FORWARD )

INCLUDE:包含訪問資源
ERROR:錯誤跳轉資源
ASYNC:異步訪問資源

可以同時配置多個

//瀏覽器直接請求和轉發訪問請求資源時  該過濾器會被執行
@WebFilter(value="/*",dispatcherTypes ={DispatcherType.FORWARD,DispatcherType.REQUEST }) 

web.xml配置
設置< dispatcher>< /dispatcher>標籤即可

過濾器鏈(配置多個過濾器)
執行順序:如果有兩個過濾器:過濾器1和過濾器2
過濾器1
過濾器2
資源執行
過濾器2
過濾器1

過濾器先後順序問題:
註解配置:按照類名的字符串比較規則比較,值小的先執行

  • 如: AFilter 和 BFilter,AFilter就先執行了。
    web.xml配置: < filter-mapping>誰定義在上邊,誰先執行

案例

案例1_登錄驗證
需求:
訪問登錄案例的資源。驗證其是否登錄
如果登錄了,則直接放行。
如果沒有登錄,則跳轉到登錄頁面,提示"您尚未登錄,請先登錄"。

package cn.kinggm520.web.filter;

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

//瀏覽器直接訪問請求資源時  該過濾器會被執行
@WebFilter(value="/*") //攔截路徑 全部資源
public class LoginFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//  強轉
        HttpServletRequest request = (HttpServletRequest)req;
//       獲取資源請求路徑
        String uri = request.getRequestURI();
//        判斷是否包含登錄相關資源
        if(uri.contains("/login.jsp")||uri.contains("/loginServlet") ||uri.contains("/css/")||uri.contains("/js/")||uri.contains("/fonts/")||uri.contains("/checkCodeServlet")){
//            包含 放行
            chain.doFilter(req, resp);
        }else {
//            不包含  驗證用戶是否登錄 獲取session
            Object manager = request.getSession().getAttribute("manager");

            if(manager!=null){
//                已登錄 放行
                chain.doFilter(req, resp);
            }else {
//                未登錄
                request.setAttribute("login_msg","您尚未登錄,請登錄");
                request.getRequestDispatcher("/login.jsp").forward(request, resp);
            }
        }
    }
    public void init(FilterConfig config) throws ServletException {

    }

}

案例2_敏感詞彙過濾
需求:
對添加案例錄入的數據進行敏感詞彙過濾
敏感詞彙爲 F U C K f u c k c a o 艹
如果是敏感詞彙,替換爲 ***

分析:
對request對象進行增強。增強獲取參數相關方法
放行。傳遞代理對象

增強對象的功能:

1. 繼承:  子類繼承父類,重寫父類方法
		缺點: 
			1. 子類必須獲取父類的構造函數.
			2. extends 位置很寶貴的
2. 裝飾者設計模式
		缺點: 需要額外創建一個類,才能實現對類的方法進行增強.
		實現:
			1. 裝飾者和被裝飾者要實現相同的接口,或者繼承同一個父類.
            2. 裝飾者必須有被裝飾者的引用.
            3. 對需要增強的方法進行增強
            4. 對不需要增強的方法,調用原來的方法即可
			
3. 動態代理  ★★★
		優點: 不需要額外的類, 動態的對類的方法進行增強. 

設計模式:一些通用的解決固定問題的方式
本次介紹(更多的設計模式看我的專欄Java常用設計模式)
代理模式 (動態代理模式)

概念:
真實對象:被代理的對象
代理對象:

代理模式:代理對象代理真實對象,達到增強真實對象功能的目的
實現方式:
靜態代理:有一個類文件描述代理模式
動態代理:在內存中形成代理類

實現步驟:
代理對象和真實對象實現相同的接口
代理對象 = Proxy.newProxyInstance();
方法裏傳3個參數

  • 1、類加載器 真實對象.getClass().getClassLoader()
  • 2、接口數組 真實對象.getClass().getInterfaces()
  • 3、處理器 匿名內部類形式的 new InvocationHandler()
    使用代理對象調用方法。

增強方法

public Object invoke(Object proxy, Method method, Object[] args)
	參數1: proxy 代理對象(慎用)
	參數2: method 表示代理對象的方法對象
	參數3: args 表示代理對象方法的參數列表
	業務邏輯:
		  判斷方法是否是需要增強的方法
			若是: 對方法的 返回值/方法體/參數進行增強
			若否: 調用原來方法
	

增強方式:
增強參數列表
增強返回值類型
增強方法體執行邏輯

案例代碼實現:

package cn.kinggm520.web.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* 敏感詞彙過濾器
*/
@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {

private List<String> list = new ArrayList<String>();//敏感詞彙集合

// init 初始化方法. 服務器啓動時會執行一次
public void init(FilterConfig config) throws ServletException {

    try {
        //1.獲取文件真實路徑
        ServletContext servletContext = config.getServletContext();
        String realPath = servletContext.getRealPath("/WEB-INF/classes/SensitiveWorlds.txt");
        //2.讀取文件
        BufferedReader br = new BufferedReader(new FileReader(realPath));
        //3.將文件的每一行數據添加到list中
        String line = null;
        while ((line = br.readLine()) != null) {
            list.add(line);
        }
        br.close();

    } catch (Exception e) {
        e.printStackTrace();
    }

}

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
    //1.創建代理對象,增強getParameter方法

    ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增強getParameter方法
        //判斷是否是getParameter方法
        if (method.getName().equals("getParameter")) {
            //增強返回值
            //獲取返回值
            String value = (String) method.invoke(req, args);// String word = request.getParameter("word");
            if (value != null) {
                for (String str : list) {
                    if (value.contains(str)) {
                        value = value.replaceAll(str, "***");
                    }
                }
            }

            return value;
        }

//判斷方法名是否是 getParameterMap
 if (method.getName().equals("getParameterMap")) {
     //增強返回值
     //獲取返回值
     Map<String, String[]> map = (Map<String, String[]>) method.invoke(req, args);
     Set<String> keySet = map.keySet();
     for (String key : keySet) {
         String[] values = map.get(key);
         if (values != null) {
             for (int i = 0; i < values.length; i++) {
                 String value = values[i];
                 for (String str : list) {
                     if (value.contains(str)) {
                         value = value.replaceAll(str, "***");
                     }
                 }
                 // 重新存入數組中
                 values[i] = value;
             }
         }

     }

     return map;
 }

//判斷方法名是否是 getParameterValue
if (method.getName().equals("getParameterValues")) {
   //增強返回值
   //獲取返回值
   String[] values = (String[]) method.invoke(req, args);
   if (values != null) {
       for (int i = 0; i < values.length; i++) {
           String value = values[i];
           for (String str : list) {
               if (value.contains(str)) {
                   value = value.replaceAll(str, "***");
               }
           }
           // 重新存入數組中
           values[i] = value;
       }
   }

   return values;
}

return method.invoke(req, args);
}
});

  //2.放行
  chain.doFilter(proxy_req, resp);
}

public void destroy() {
}

}

二、Listener:監聽器

概念:web的三大組件之一。
事件監聽機制
事件 :一件事情
事件源 :事件發生的地方
監聽器 :一個對象
註冊監聽:將事件、事件源、監聽器綁定在一起。 當事件源上發生某個事件後,執行監聽器代碼

ServletContextListener:監聽ServletContext對象的創建和銷燬
方法:
void contextDestroyed(ServletContextEvent sce) :ServletContext對象被銷燬之前會調用該方法
void contextInitialized(ServletContextEvent sce) :ServletContext對象創建後會調用該方法

步驟:
定義一個類,實現ServletContextListener接口
複寫方法

/**
 * 作者: kinggm Email:[email protected]
 * 時間:  2020-03-02 19:06
 */

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

@WebListener()
public class Listener implements ServletContextListener {


    //在服務器啓動後自動調用
    public void contextInitialized(ServletContextEvent sce) {
//         加載資源文件
        ServletContext servletContext = sce.getServletContext();
//        加載資源文件
        String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
//獲取真實路徑
        String realPath = servletContext.getRealPath("contextConfigLocation");

//        加載進內存
        try {
            FileInputStream fileInputStream = new FileInputStream(realPath);
            System.out.println(fileInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("ServletContext對象被創建了...");
    }

    //    當服務器正常關閉後該方法被調用
    public void contextDestroyed(ServletContextEvent sce) {

        System.out.println("ServletContext對象被銷燬了...");
    }


}

程序執行結果:
ServletContext對象被創建了…
ServletContext對象被銷燬了…

一般用於加載全局資源文件

配置
web.xml
< listener>
< listener-class>cn.itcast.web.listener.ContextLoaderListener< /listener-class>
< /listener>

指定初始化參數< context-param>

< context-param>
< param-name>contextConfigLocation< /param-name>
< param-value>/WEB-INF/classes/applicationContext.xml< /param-value>
< /context-param>

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