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

「別小看任何人,越不起眼的人。每每會作些讓人想不到的事。"你好我是夢陽辰,快來和我一塊兒學習吧!css


文章目錄

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


01.Filter概述

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

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

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

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

過濾器的做用:
通常完成通用性的操做。
好比:登陸驗證,判斷用戶是否登陸;統一編碼處理,敏感字符處理等。瀏覽器

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

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

02.Filter快速入門

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

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) {

    }}

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

在這裏插入圖片描述

相關文章
相關標籤/搜索