過濾器是web開發中常用的開發方式,比如一些典型的應用場景:
用戶身份認證、對用戶請求進行記錄和審核、對用戶發送的數據進行替換和過濾、轉換圖像格式、對響應內容壓縮、加密請求或響應等等。
本篇就了解下監聽器的主要使用方法。
什么是過濾器?
過濾器的生命周期
過濾器的生命周期與web容器相同,當web容器啟動時,就會讀取應用的web.xml配置文件,如果這里配置了過濾器,容器就會執行實例化,並調用過濾器的init方法。
之后用戶的每一次請求都會執行過濾器的doFilter方法。
當web容器銷毀時,就會執行destroy方法,釋放資源。
過濾器的執行過程
過濾器看名字就能知道大概的用法,它就像一個篩子,可以篩選特定的數據或請求。執行過程如下圖所示:
用戶在發送請求后,如果該請求滿足過濾器的過濾規則,web容器就會執行過濾器中的doFilter方法進行特定的操作;然后通過調用FilterChain.doFilter轉交給web容器。web容器執行完成后把資源返回給過濾器,再展現給用戶。
簡單的過濾器實例
下面通過一個簡單的代碼,看看過濾器的編寫。
首先,需要創建一個過濾器,過濾器集成javax.servlet.Filter接口,其中必須實現三個方法:init() doFilter() destroy()
public class MyFilter implements Filter{ public void destroy() { System.out.println("MyFilter destroy"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter start...dofilter"); chain.doFilter(request, response);//對請求放行 System.out.println("MyFilter end...dofilter"); } public void init(FilterConfig arg0) throws ServletException { System.out.println("MyFilter init"); } }
init()方法是在web容器實例化過濾器時調用的。
doFilter()方法是每次有請求,且滿足過濾規則時調用。
destroy()方法是web容器關閉時,調用。
然后,在web.xml中配置相應的選項。如果是servlet3.0,那么支持注解的方式配置過濾器。
<filter> <filter-name>MyFilter</filter-name> <filter-class>com.test.filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/index.jsp</url-pattern> <!--<dispatcher></dispatcher>--> </filter-mapping>
其中幾個必備的項:
在<filter>中配置了過濾器,filter-name是過濾器的名字,filter-class是過濾器的類;
在<filter-mapping>中配置了過濾器的映射規則,filter-name是過濾器的名字,url-pattern是過濾的規則,dispatcher是過濾器的分類(主要包括四種,稍后講解)
這里先說下過濾器的規則,如果想要全部的請求都過濾,那么可以寫/*
如果想要過濾index.jsp index.html 可以寫/index*
如果只想過濾index.jsp,可以寫成/index.jsp
其次,配置好后,創建index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> This is Filter JSP! <% System.out.println("index jsp"); %> </body> </html>
最后,當啟動web容器后,可以在控制台中發現,初始化時,執行了init方法
訪問對應的web資源,可以看到控制台按照執行的順序打印消息:
多個過濾器操作
多個過濾器執行與上面差不多。
在上面代碼的基礎上,再增加一個過濾器:
public class SecondFilter implements Filter{ public void destroy() { System.out.println("SecondFilter destroy()"); } public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException { System.out.println("SecondFilter doFilter start"); chain.doFilter(arg0, arg1); System.out.println("SecondFilter doFilter end"); } public void init(FilterConfig arg0) throws ServletException { System.out.println("SecondFilter init()"); } }
在web.xml中增加過濾器配置
<filter> <filter-name>SecondFilter</filter-name> <filter-class>com.test.filter.SecondFilter</filter-class> </filter> <filter-mapping> <filter-name>SecondFilter</filter-name> <url-pattern>/index.jsp</url-pattern> </filter-mapping>
啟動web容器,控制台輸出init信息
訪問頁面,可以看到由於在web.xml中映射配置MyFilter在SecondFilter上面,
因此輸出如下消息:
類似的,如果把SecondFilter配置放在上面,就會先執行SecondFilter的doFilter方法。
過濾器的分類
最后看一下過濾器的分類,過濾器主要包括四種,REQUEST\FORWARD\INCLUDE\ERROR(3.0額外新增了一個異步請求ASYNC)。
上面的過濾器都是采用REQUEST的方式,直接請求。由於沒有配置dispathcer,默認就會按照REQUEST的方式進行。
直接看一下FORWARD的使用方法,在doFilter中執行下面的方法:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter start...dofilter"); HttpServletRequest req = (HttpServletRequest)request; req.getRequestDispatcher("main.jsp").forward(request, response); System.out.println("MyFilter end...dofilter"); }
此時頁面請求就會直接跳轉到main.jsp,觸發FORWARD類型過濾器,過濾器配置選項如下:
<filter> <filter-name>MyFilter</filter-name> <filter-class>com.test.filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/index.jsp</url-pattern> </filter-mapping> <filter> <filter-name>SecondFilter</filter-name> <filter-class>com.test.filter.SecondFilter</filter-class> </filter> <!-- <filter-mapping> <filter-name>SecondFilter</filter-name> <url-pattern>/index.jsp</url-pattern> </filter-mapping> --> <filter-mapping> <filter-name>SecondFilter</filter-name> <url-pattern>/main.jsp</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping>
啟動后發現,原本請求Index.jsp跳轉到了main.jsp。
此時,如果第二個過濾器采用的是REQUEST,就不會觸發了。
另外,還可以使用JSP頁面標簽,執行跳轉,此時過濾器也可以觸發。比如在JSP頁面中添加<jsp:forward>標簽
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<jsp:forward page="/main.jsp"></jsp:forward>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> </body> </html>
INCLUDE與FORWARD類似,使用方法也相同,只是名字不同而已,就不做過多的介紹了。
然后看一下ERROR過濾器,通常我們會在web.xml中配置錯誤頁面,如下:
<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page>
此時,如果訪問不存在的頁面,http://localhost:8080/xxx.jsp就會跳轉到error.jsp
此時如果過濾器通過REQUEST方式,想要觸發,url填寫的是/error.jsp並不會起作用,此時就需要把dispathcer改成 ERROR,並且放置在error-page標簽下面:
<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <!-- 需要放在errorpage下面 --> <filter> <filter-name>ErrorFilter</filter-name> <filter-class>com.test.filter.ErrorFilter</filter-class> </filter> <filter-mapping> <filter-name>ErrorFilter</filter-name> <url-pattern>/error.jsp</url-pattern> <dispatcher>ERROR</dispatcher> </filter-mapping>
這樣就會觸發ERROR過濾器了。
參考
【1】過濾器的應用:http://www.ylzx8.cn/web/web/979338.html
【2】過濾器視頻教程:http://www.imooc.com/learn/213