【Java】Filter和Listener

Filter:過濾器

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

在這裏插入圖片描述

Filter快速入門

快速入門:
 1. 步驟:
    1. 定義一個類,實現接口Filter
    2. 複寫方法
    3. 配置攔截路徑
        1. web.xml
        2. 註解
 2. 代碼:
  @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("filterDemo1被執行了....");
           //放行
          filterChain.doFilter(servletRequest,servletResponse)}
  
      @Override
      public void destroy() {
  
      }
  }

過濾器細節問題

 1. 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>
2. 過濾器執行流程
  1. 執行過濾器
  2. 執行放行後的資源
  3. 回來執行過濾器放行代碼下邊的代碼
 3. 過濾器生命週期方法
  1. init:在服務器啓動後,會創建Filter對象,然後調用init方法。只執行一次。用於加載資源
  2. doFilter:每一次請求被攔截資源時,會執行。執行多次
  3. destroy:在服務器關閉後,Filter對象被銷燬。如果服務器是正常關閉,則會執行destroy方法。只執行一次。用於釋放資源
package cn.itcast.web.filter;

import javax.servlet.*;
import java.io.IOException;

/*@WebFilter("/*")*/
public class FilterDemo3 implements Filter {

    /**
     * 每一次請求被攔截資源時,會執行。執行多次
     * @param req
     * @param resp
     * @param chain
     * @throws ServletException
     * @throws IOException
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("doFilter....");
        chain.doFilter(req, resp);
    }

    /**
     * 在服務器啓動後,會創建Filter對象,然後調用init方法。只執行一次。用於加載資源
     * @param config
     * @throws ServletException
     */
    public void init(FilterConfig config) throws ServletException {
        System.out.println("init....");
    }

    /**
     * 在服務器關閉後,Filter對象被銷燬。如果服務器是正常關閉,則會執行destroy方法。只執行一次。用於釋放資源
     */
    public void destroy() {
        System.out.println("destroy....");
    }

}
 4. 過濾器配置詳解
  * 攔截路徑配置:
   1. 具體資源路徑: /index.jsp   只有訪問index.jsp資源時,過濾器纔會被執行
   2. 攔截目錄: /user/* 訪問/user下的所有資源時,過濾器都會被執行
   3. 後綴名攔截: *.jsp  訪問所有後綴名爲jsp資源時,過濾器都會被執行
   4. 攔截所有資源:/*  訪問所有資源時,過濾器都會被執行
  * 攔截方式配置:資源被訪問的方式
   * 註解配置:
    * 設置dispatcherTypes屬性
     1. REQUEST:默認值。瀏覽器直接請求資源
     2. FORWARD:轉發訪問資源
     3. INCLUDE:包含訪問資源
     4. ERROR:錯誤跳轉資源
     5. ASYNC:異步訪問資源
   * web.xml配置
    * 設置<dispatcher></dispatcher>標籤即可
package cn.itcast.web.filter;

import javax.servlet.*;
import java.io.IOException;

//@WebFilter("/index.jsp") //1. 具體資源路徑: /index.jsp   只有訪問index.jsp資源時,過濾器纔會被執行
//@WebFilter("/user/*") //2. 攔截目錄: /user/* 訪問/user下的所有資源時,過濾器都會被執行
//@WebFilter("*.jsp")   //3. 後綴名攔截: *.jsp     訪問所有後綴名爲jsp資源時,過濾器都會被執行
public class FilterDemo4 implements Filter {


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("filterDemo4....");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

    public void destroy() {
    }

}
package cn.itcast.web.filter;

import javax.servlet.*;
import java.io.IOException;

//瀏覽器直接請求index.jsp資源時,該過濾器會被執行
//@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.REQUEST)
//只有轉發訪問index.jsp時,該過濾器纔會被執行
//@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.FORWARD)

//瀏覽器直接請求index.jsp或者轉發訪問index.jsp。該過濾器纔會被執行
//@WebFilter(value="/*",dispatcherTypes ={ DispatcherType.FORWARD,DispatcherType.REQUEST})
public class FilterDemo5 implements Filter {


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("filterDemo5....");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

    public void destroy() {
    }

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

		* 過濾器先後順序問題:
			1. 註解配置:按照類名的字符串比較規則比較,值小的先執行
				* 如: AFilter 和 BFilter,AFilter就先執行了。
			2. web.xml配置: <filter-mapping>誰定義在上邊,誰先執行

過濾器案例

案例1
在這裏插入圖片描述

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

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

/**
 * 登錄驗證的過濾器
 */
@WebFilter("/*")
public class LoginFilter implements Filter {


    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println(req);
        //0.強制轉換
        HttpServletRequest request = (HttpServletRequest) req;

        //1.獲取資源請求路徑
        String uri = request.getRequestURI();
        //2.判斷是否包含登錄相關資源路徑,要注意排除掉 css/js/圖片/驗證碼等資源
        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{
            //不包含,需要驗證用戶是否登錄
            //3.從獲取session中獲取user
            Object user = request.getSession().getAttribute("user");
            if(user != null){
                //登錄了。放行
                chain.doFilter(req, resp);
            }else{
                //沒有登錄。跳轉登錄頁面

                request.setAttribute("login_msg","您尚未登錄,請登錄");
                request.getRequestDispatcher("/login.jsp").forward(request,resp);
            }
        }


        // chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

    public void destroy() {
    }

}

案例2
在這裏插入圖片描述

在介紹案例前先介紹一下代理模式
在這裏插入圖片描述

package cn.itcast.proxy;

public interface SaleComputer {

    public String sale(double money);

    public void show();
}
package cn.itcast.proxy;

/**
 * 真實類
 */
public class Lenovo implements SaleComputer {
    @Override
    public String sale(double money) {

        System.out.println("花了"+money+"元買了一臺聯想電腦...");
        return "聯想電腦";
    }

    @Override
    public void show() {
        System.out.println("展示電腦....");
    }
}
package cn.itcast.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {

    public static void main(String[] args) {
        //1.創建真實對象
        Lenovo lenovo = new Lenovo();
        
        //2.動態代理增強lenovo對象
        /*
            三個參數:
                1. 類加載器:真實對象.getClass().getClassLoader()
                2. 接口數組:真實對象.getClass().getInterfaces()
                3. 處理器:new InvocationHandler()
         */
        SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {

            /*
                代理邏輯編寫的方法:代理對象調用的所有方法都會觸發該方法執行
                    參數:
                        1. proxy:代理對象
                        2. method:代理對象調用的方法,被封裝爲的對象
                        3. args:代理對象調用的方法時,傳遞的實際參數
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /*System.out.println("該方法執行了....");
                System.out.println(method.getName());
                System.out.println(args[0]);

*/
                //判斷是否是sale方法
                if(method.getName().equals("sale")){
                    //1.增強參數
                    double money = (double) args[0];
                    money = money * 0.85;
                    System.out.println("專車接你....");
                    //使用真實對象調用該方法
                    String obj = (String) method.invoke(lenovo, money);
                    System.out.println("免費送貨...");
                    //2.增強返回值
                    return obj+"_鼠標墊";
                }else{
                    Object obj = method.invoke(lenovo, args);
                    return obj;
                }

            }
        });

        //3.調用方法

       /* String computer = proxy_lenovo.sale(8000);
        System.out.println(computer);*/

        proxy_lenovo.show();
    }
}
    2. 案例2_敏感詞彙過濾
		* 需求:
			1. 對day17_case案例錄入的數據進行敏感詞彙過濾
			2. 敏感詞彙參考《敏感詞彙.txt》
			3. 如果是敏感詞彙,替換爲 *** 

		* 分析:
			1. 對request對象進行增強。增強獲取參數相關方法
			2. 放行。傳遞代理對象
		* 增強對象的功能:
			* 設計模式:一些通用的解決固定問題的方式
			1. 裝飾模式
			2. 代理模式
				* 概念:
					1. 真實對象:被代理的對象
					2. 代理對象:
					3. 代理模式:代理對象代理真實對象,達到增強真實對象功能的目的
			 	* 實現方式:
				 	1. 靜態代理:有一個類文件描述代理模式
				 	2. 動態代理:在內存中形成代理類
						* 實現步驟:
							1. 代理對象和真實對象實現相同的接口
							2. 代理對象 = Proxy.newProxyInstance();
							3. 使用代理對象調用方法。
							4. 增強方法

						* 增強方式:
							1. 增強參數列表
							2. 增強返回值類型
							3. 增強方法體執行邏輯	
package cn.itcast.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;

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


    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);
                    if(value != null){
                        for (String str : list) {
                            if(value.contains(str)){
                                value = value.replaceAll(str,"***");
                            }
                        }
                    }
                    
                    return  value;
                }

                //判斷方法名是否是 getParameterMap

                //判斷方法名是否是 getParameterValue

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

        //2.放行
        chain.doFilter(proxy_req, resp);
    }
    private List<String> list = new ArrayList<String>();//敏感詞彙集合
    public void init(FilterConfig config) throws ServletException {

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

            br.close();

            System.out.println(list);

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

    }

    public void destroy() {
    }

}

監聽器:Listener

* 概念:web的三大組件之一。
	* 事件監聽機制
		* 事件	:一件事情
		* 事件源 :事件發生的地方
		* 監聽器 :一個對象
		* 註冊監聽:將事件、事件源、監聽器綁定在一起。 當事件源上發生某個事件後,執行監聽器代碼
* ServletContextListener:監聽ServletContext對象的創建和銷燬
	* 方法:
		* void contextDestroyed(ServletContextEvent sce) :ServletContext對象被銷燬之前會調用該方法
		* void contextInitialized(ServletContextEvent sce) :ServletContext對象創建後會調用該方法
	* 步驟:
		1. 定義一個類,實現ServletContextListener接口
		2. 複寫方法
		3. 配置
			1. web.xml
					<listener>
 					 <listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class></listener>

	* 指定初始化參數<context-param>  在web.xml當中添加該參數
			2. 註解:
				* @WebListener
package cn.itcast.web.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.FileInputStream;


@WebListener
public class ContextLoaderListener implements ServletContextListener {

    /**
     * 監聽ServletContext對象創建的。ServletContext對象服務器啓動後自動創建。
     *
     * 在服務器啓動後自動調用
     * @param servletContextEvent
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //加載資源文件
        //1.獲取ServletContext對象
        ServletContext servletContext = servletContextEvent.getServletContext();

        //2.加載資源文件
        String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");

        //3.獲取真實路徑
        String realPath = servletContext.getRealPath(contextConfigLocation);

        //4.加載進內存
        try{
            FileInputStream fis = new FileInputStream(realPath);
            System.out.println(fis);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("ServletContext對象被創建了。。。");
    }

    /**
     * 在服務器關閉後,ServletContext對象被銷燬。當服務器正常關閉後該方法被調用
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext對象被銷燬了。。。");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章