Servlet高級Fitter(過濾器)&Listener(監聽器)

“別小看任何人,越不起眼的人。往往會做些讓人想不到的事。"你好我是夢陽辰,快來和我一起學習吧!


文章目錄

  • 01.Filter概述
  • 02.Filter快速入門
  • 03.Filter深入
  • 04.Filter案例
  • 05.動態代理
  • 06.Listener概述
  • 07.ServletContextListener


01.Filter概述

Filter和Listener是Servlet規範中的兩個高級特性,不同於Servlet,他們不用於處理客戶端請求。

Filter用於對request,response對象進行修改
Filter被稱作過濾器,其基本功能就是對Servlet容器調用Servlet的過程進行攔截,從而在Servlet進行響應處理前後實現一些特殊的功能。這就好比現實中的污水淨化設備,它可以看作一個過濾器,專門用於過濾污水雜質。

當瀏覽器訪問服務器中的目標資源時,會被Filter攔截,在Filter中進行預處理操作,然後再將請求轉發給目標資源。

當服務器接收到這個請求後會對其進行響應,在服務器處理響應的過程中,也需要先將響應結果發送給過濾器,在過濾器中對響應結果進行處理後,纔會發送給客戶端。

過濾器的作用:
一般完成通用性的操作。
比如:登錄驗證,判斷用戶是否登錄;統一編碼處理,敏感字符處理等。

其實Filter過濾器就是實現了javax.servlet.Filter接口的類,在javax.servlet.Filter定義了三個方法。

Listener用於對context,session,request事件進行監聽。

02.Filter快速入門

步驟:
1.定義一個類,實現接口javax.servlet.Filter。

2.複寫方法

3.配置攔截路徑
3.1.web.xml配置
3.2.註解配置
javax.servlet.Filter定義了三個方法


@WebFilter("/*")//攔截所有資源public class FilterTest1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //初始化過濾器,在Web程序加載的時候調用,配置初始化參數    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.print("我是過濾器!");

        //是否放行,即轉發到請求資源
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        //用於釋放被Filter打開的資源,當Web服務器卸載Filter對象之前被調用    }}

web.xml配置
取消註解配置,使用web.xml配置。

 <filter>
        <filter-name>FilterTest1</filter-name>
        <filter-class>filter.FilterTest1</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>FilterTest1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

03.Filter深入

過濾器的流程:

@WebFilter("/*")public class FilterTest2 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //用於攔截用戶的請求,如果和當前過濾器的攔截路徑匹配,此方法會被調用
        //對request對象請求消息增強
        System.out.println("我被執行了request!");

        chain.doFilter(req, resp);

        //對response對象的響應消息增強
        System.out.println("我被又執行了response!");
    }

    public void init(FilterConfig config) throws ServletException {

    }}

攔截路徑的配置:
具體資源路徑:/index.jsp 只有訪問index.jsp資源時,過濾器纔會被執行。

2.攔截目錄:/user/* 訪問/uer下的所有資源時,過濾器都會被執行。

3.後綴名攔截:*.jsp 訪問所有後綴名爲jsp資源時,過濾器執行。

4.攔截所有資源:/*
攔截方式的配置:

資源被訪問的方式:
請求轉發過濾器不會被執行。
1.註解配置,可以配置多個值
設置dispatcherTypes屬性


REQUEST:默認值,瀏覽器直接請求資源
FORWARD:轉發訪問資源
INCLUDE:包含訪問資源
ERROR:錯誤跳轉資源
ASYNC:異步訪問資源

2.web.xml註釋

//瀏覽器直接請求資源時,會被執行,轉發不會@WebFilter(value = "/*",dispatcherTypes = DispatcherType.REQUEST)public class FilterTest3 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("我被執行啦!");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }}
@WebFilter(value = "/*",
	dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
<filter>
        <filter-name>FilterTest1</filter-name>
        <filter-class>filter.FilterTest1</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>FilterTest1</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

過濾器鏈(多個過濾器):
在這裏插入圖片描述

執行順序
先執行過濾器1,再執行過濾器2,回來先執行過濾器2,再執行過濾器1。

怎麼判斷過濾器誰在前面:
1.註解配置
按照類名的字符串比較規則,較小的先執行。
如:AFilter,BFilter
AFilter就先執行。



2.web.xml配置:

<filter-mapping>

誰定義在上面,誰就先執行。

04.Filter案例

案例一:登錄驗證
1.訪問某些資源,驗證其是否登錄

2.如果登錄了,則直接放行。

3.如果沒有登錄,則跳轉到登錄頁面,提示,“你尚未登錄,請先登錄”。
在這裏插入圖片描述

/**
 * 登錄驗證的過濾器
 */@WebFilter("/*")//除了登錄相關資源外(如login.jsp)public class FilterTest4 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //1.強轉
        HttpServletRequest request = (HttpServletRequest)req;
        //2.獲取資源請求路徑
        String uri = request.getRequestURI();
        //排除登錄相關資源,css/js/fonts等        if(uri.contains("/login.jsp")||uri.contains("/loginServlet")||uri.contains("/css/")||uri.contains("/js/")){
            //用戶就是像登錄,放行
            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");
            }
        }

    }

    public void init(FilterConfig config) throws ServletException {

    }}

案例二:敏感詞彙的過濾
分析:
在這裏插入圖片描述
案例需要對request對象進行增強。


那如進行增強呢?
增強對象的功能
設計模式:一些通用的解決固定問題的方式。
裝飾模式:


代理模式:
概念:
1.真實對象:被代理的對象。
2.代理對象
3.代理模式:代理對象代理真實對象,達到增強真實對象的目的。
實現方式:
1.靜態代理
在一個類文件描述代理模式。






2.動態代理
在內存中形成代理類(在內存中動態生成)。
在這裏插入圖片描述
注:請先看以下動態代理相關知識再看案例二的實現
案例二實現:
1.對requset對象進行增強,增強獲取參數相關方法。
2.放行,傳遞代理對象。
代碼實現:






/**
 * 敏感詞彙過濾器
 */@WebFilter("/*")public class FilterSensitiveWords implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //創建代理對象,增強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方法                //判斷是否是該方法                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;
                }
                return method.invoke(req,args);
            }
        });
        //2.放行
        chain.doFilter(proxy_req, resp);
    }
    private List<String> list = new ArrayList<>();//敏感詞彙    public void init(FilterConfig config) throws ServletException {

        try {
            //1.加載配置文件(獲取文件的真實路徑)
            ServletContext servletContext = config.getServletContext();
            String realPath = servletContext.getRealPath("/WEB-INF/classes/SensitiveWords.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 (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }}

測試的servlet:

@WebServlet("/ServletSensitiveWordsTest")public class ServletSensitiveWordsTest extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        String msg =request.getParameter("msg");
        System.out.println(name+":"+msg);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }}

結果:
在這裏插入圖片描述
重點:注意路徑問題:
SensitiveWords在src源文件目錄下。
//1.加載配置文件(獲取文件的真實路徑)
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath
("/WEB-INF/classes/SensitiveWords.txt");






05.動態代理

實現步驟:
1.代理對象和真實對象實現相同的接口。

2.代理對象= Proxy.newProxyInstance();

3.使用代理對象調用方法。

4.增強方法。
增強方式:
1.增強參數列表。

2.增強返回值類型。

3.增強方法體執行邏輯。
練習理解:

package proxy;public interface SaleComputer {
    public String sale(double money);
    public void show();}
/**
 * 真實類
 */public class ASUS implements SaleComputer{
    @Override
    public String sale(double money) {
        System.out.println("花了"+money+"拍下一臺電腦!");
        return "ASUS";
    }

    @Override
    public void show() {
        System.out.println("展示電腦!");
    }}
public class ProxyTest {
    public static void main(String[] args) {
        //創建真實對象
        ASUS asus1 = new ASUS();
        //動態代理增強ASUS對象
        /*
        三個參數:
        1.類加載器:真實對象.getClass().getClassLoader()
        2.接口數組:真實對象.getClass().getInterfaces()
        3.處理器:new InvocationHandler()
         */
        SaleComputer proxy_asus=(SaleComputer) Proxy.newProxyInstance(asus1.getClass().getClassLoader(), asus1.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 代理邏輯編寫的方法:代理對象調用的所有方法都會觸發該方法執行
             * @param proxy 代理對象
             * @param method 代理對象調用的方法被封裝成對象
             * @param args 代理對象調用方法時,傳遞的實際參數
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /*System.out.println("該方法執行了...");
                System.out.println(method.getName());*/
                //使用真實對象調用該方法
               /* Object obj = method.invoke(asus1,args);*/

                //增強參數;判斷是否時sale方法
                if(method.getName().equals("sale")){
                    //增強參數
                    double money = (double)args[0];
                    money*=0.8;
                    //使用真實對象調用該方法
                    String  obj = (String) method.invoke(asus1,money);

                    //增強返回值類型

                    return  obj+"_鼠標墊";
                }else {
                    Object obj = method.invoke(asus1,args);
                    return obj;
                }

            }
        });

        //3.調用方法
        String computer = proxy_asus.sale(9000);
        System.out.println(computer);
    }}

06.Listener概述

概念:web的三大組件之一。
事件監聽機制
程序開發中,經常需要對某些事件進行監聽,如鼠標單擊事件,監聽鍵盤按下事件等,此時就需要監聽器。

事件:用戶的一個操作,如點擊按鈕…

事件源:產生事件的對象。

監聽器:負責監聽發生在事件源上的事件。

註冊監聽:將事件,事件源,監聽器綁定在一起。當事件源上發生某個事件後,執行監聽器代碼。

ServletContextListener:監聽ServletContext對象的創建和銷燬。

//Servlet對象被銷燬前會調用此方法void contextDestroyed(ServletContextEvent sce) //ServletContext對象被創建後會調用該方法。void contextInitialized(ServletContextEvent sce)

07.ServletContextListener

步驟:
1.定義一個類實現ServletContextListener接口。

2.複寫方法。

3.配置
1.web.xml

<listener>
        <listener-class>listener/ServletContextListenerTest1</listener-class>
    </listener>

指定初始化參數:

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

2.註解配置

@WebListener
@WebListenerpublic class ServletContextListenerTest1 implements ServletContextListener {
    //監聽ServletContext創建,ServletContext對象服務器啓動自動創建
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //加載資源文件
        //1.獲取ServletContext對象
        ServletContext servletContext = sce.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 (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    //在服務器關閉後,ServletContext對象被銷燬。當服務器正常關閉後該方法被調用
    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }}

一切事無法追求完美,唯有追求盡力而爲。這樣心無壓力,出來的結果反而會更好。

在這裏插入圖片描述


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