1 什么是過濾器
過濾器JavaWeb三大組件之一,它與Servlet很相似!不它過濾器是用來攔截請求的,而不是處理請求的。
當用戶請求某個Servlet時,會先執行部署在這個請求上的Filter,如果Filter“放行”,那么會繼承執行用戶請求的Servlet;如果Filter不“放行”,那么就不會執行用戶請求的Servlet。
其實可以這樣理解,當用戶請求某個Servlet時,Tomcat會去執行注冊在這個請求上的Filter,然后是否“放行”由Filter來決定。可以理解為,Filter來決定是否調用Servlet!當執行完成Servlet的代碼后,還會執行Filter后面的代碼。
?
其實過濾器與Servlet很相似,我們回憶一下如果寫的第一個Servlet應用!寫一個類,實現Servlet接口!沒錯,寫過濾器就是寫一個類,實現Filter接口。
public class HelloFilter implements Filter {public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {System.out.println("Hello Filter");}public void destroy() {} }
<filter><filter-name>helloFilter</filter-name><filter-class>cn.itcast.filter.HelloFilter</filter-class></filter><filter-mapping><filter-name>helloFilter</filter-name><url-pattern>/index.jsp</url-pattern></filter-mapping>
過濾器的生命周期
?
我們已經學習過Servlet的生命周期,那么Filter的生命周期也就沒有什么難度了!
?
init(FilterConfig):在服務器啟動時會創建Filter實例,并且每個類型的Filter只創建一個實例,從此不再創建!在創建完Filter實例后,會馬上調用init()方法完成初始化工作,這個方法只會被執行一次;
?
doFilter(ServletRequest req,ServletResponse res,FilterChain chain):這個方法會在用戶每次訪問“目標資源(<url->pattern>index.jsp</url-pattern>)”時執行,如果需要“放行”,那么需要調用FilterChain的doFilter(ServletRequest,ServletResponse)方法,如果不調用FilterChain的doFilter()方法,那么目標資源將無法執行;
?
destroy():服務器會在創建Filter對象之后,把Filter放到緩存中一直使用,通常不會銷毀它。一般會在服務器關閉時銷毀Filter對象,在銷毀Filter對象之前,服務器會調用Filter對象的destory()方法。
?
?
?
FilterConfig
你已經看到了吧,Filter接口中的init()方法的參數類型為FilterConfig類型。它的功能與ServletConfig相似,與web.xml文件中的配置信息對應。下面是FilterConfig的功能介紹:
?ServletContext getServletContext():獲取ServletContext的方法;
String getFilterName():獲取Filter的配置名稱;與<filter-name>元素對應;
String getInitParameter(String name):獲取Filter的初始化配置,與<init-param>元素對應;
?Enumeration getInitParameterNames():獲取所有初始化參數的名稱。
?
FilterChain
doFilter()方法的參數中有一個類型為FilterChain的參數,它只有一個方法:doFilter(ServletRequest,ServletResponse)。
前面我們說doFilter()方法的放行,讓請求流訪問目標資源!但這么說不嚴密,其實調用該方法的意思是,“我(當前Filter)”放行了,但不代表其他人(其他過濾器)也放行。
也就是說,一個目標資源上,可能部署了多個過濾器,就好比在你去北京的路上有多個打劫的匪人(過濾器),而其中第一伙匪人放行了,但不代表第二伙匪人也放行了,所以調用FilterChain類的doFilter()方法表示的是執行下一個過濾器的doFilter()方法,或者是執行目標資源!
如果當前過濾器是最后一個過濾器,那么調用chain.doFilter()方法表示執行目標資源,而不是最后一個過濾器,那么chain.doFilter()表示執行下一個過濾器的doFilter()方法。
?
多個過濾器執行順序
一個目標資源可以指定多個過濾器,過濾器的執行順序是在web.xml文件中的部署順序:
?
<filter><filter-name>myFilter1</filter-name><filter-class>cn.itcast.filter.MyFilter1</filter-class></filter><filter-mapping><filter-name>myFilter1</filter-name><url-pattern>/index.jsp</url-pattern></filter-mapping><filter><filter-name>myFilter2</filter-name><filter-class>cn.itcast.filter.MyFilter2</filter-class></filter><filter-mapping><filter-name>myFilter2</filter-name><url-pattern>/index.jsp</url-pattern></filter-mapping>
public class MyFilter1 extends HttpFilter {public void doFilter(HttpServletRequest request, HttpServletResponse response,FilterChain chain) throws IOException, ServletException {System.out.println("filter1 start...");chain.doFilter(request, response);//放行,執行MyFilter2的doFilter()方法System.out.println("filter1 end...");} }
public class MyFilter2 extends HttpFilter {public void doFilter(HttpServletRequest request, HttpServletResponse response,FilterChain chain) throws IOException, ServletException {System.out.println("filter2 start...");chain.doFilter(request, response);//放行,執行目標資源System.out.println("filter2 end...");} }
<body>This is my JSP page. <br><h1>index.jsp</h1><%System.out.println("index.jsp"); %></body>
?
?
輸出結果
filter1 start...
filter2 start...
index.jsp
?
四種攔截方式
?
分別是:REQUEST、FORWARD、INCLUDE、ERROR。
?
?REQUEST:直接訪問目標資源時執行過濾器。包括:在地址欄中直接訪問、表單提交、超鏈接、重定向,只要在地址欄中可以看到目標資源的路徑,就是REQUEST;
?
FORWARD:轉發訪問執行過濾器。包括RequestDispatcher#forward()方法、<jsp:forward>標簽都是轉發訪問;
?
INCLUDE:包含訪問執行過濾器。包括RequestDispatcher#include()方法、<jsp:include>標簽都是包含訪問;
?
ERROR:當目標資源在web.xml中配置為<error-page>中時,并且真的出現了異常,轉發到目標資源時,會執行過濾器。
?
可以在<filter-mapping>中添加0~n個<dispatcher>子元素,來說明當前訪問的攔截方式。
<filter-mapping><filter-name>myfilter</filter-name><url-pattern>/b.jsp</url-pattern><dispatcher>REQUEST</dispatcher><dispatcher>FORWARD</dispatcher></filter-mapping><filter-mapping><filter-name>myfilter</filter-name><url-pattern>/b.jsp</url-pattern></filter-mapping><filter-mapping><filter-name>myfilter</filter-name><url-pattern>/b.jsp</url-pattern><dispatcher>FORWARD</dispatcher></filter-mapping>
過濾器的應用場景
過濾器的應用場景:
執行目標資源之前做預處理工作,例如設置編碼,這種試通常都會放行,只是在目標資源執行之前做一些準備工作;[c1]?
通過條件判斷是否放行,例如校驗當前用戶是否已經登錄,或者用戶IP是否已經被禁用;
?在目標資源執行后,做一些后續的特殊處理工作,例如把目標資源輸出的數據進行處理[c2]?;
?[c1]幾乎是的Sevlet中都需要寫request.setCharacterEndoing() 可以把它入到一個Filter中
?[c2]回程攔截!
?
?
設置目標資源
在web.xml文件中部署Filter時,可以通過“*”來執行目標資源:
<filter-mapping><filter-name>myfilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
?
這一特性與Servlet完全相同!通過這一特性,我們可以在用戶訪問敏感資源時,執行過濾器,例如:<url-pattern>/admin/*<url-pattern>,可以把所有管理員才能訪問的資源放到/admin路徑下,這時可以通過過濾器來校驗用戶身份。
還可以為<filter-mapping>指定目標資源為某個Servlet,例如:
<servlet><servlet-name>myservlet</servlet-name><servlet-class>cn.itcast.servlet.MyServlet</servlet-class></servlet><servlet-mapping><servlet-name>myservlet</servlet-name><url-pattern>/abc</url-pattern></servlet-mapping><filter><filter-name>myfilter</filter-name><filter-class>cn.itcast.filter.MyFilter</filter-class></filter><filter-mapping><filter-name>myfilter</filter-name><servlet-name>myservlet</servlet-name></filter-mapping>
當用戶訪問http://localhost:8080/filtertest/abc時,會執行名字為myservlet的Servlet,這時會執行過濾器。
?
Filter小結
Filter的三個方法:
void init(FilterConfig):在Tomcat啟動時被調用;
void destroy():在Tomcat關閉時被調用;
void doFilter(ServletRequest,ServletResponse,FilterChain):每次有請求時都調用該方法;
?
FilterConfig類:與ServletConfig相似,用來獲取Filter的初始化參數
ServletContext getServletContext():獲取ServletContext的方法;
String getFilterName():獲取Filter的配置名稱;
String getInitParameter(String name):獲取Filter的初始化配置,與<init-param>元素對應;
Enumeration getInitParameterNames():獲取所有初始化參數的名稱。
?
FilterChain類:
void doFilter(ServletRequest,ServletResponse):放行!表示執行下一個過濾器,或者執行目標資源。可以在調用FilterChain的doFilter()方法的前后添加語句,在FilterChain的doFilter()方法之前的語句會在目標資源執行之前執行,在FilterChain的doFilter()方法之后的語句會在目標資源執行之后執行。
?
四個攔截方式:REQUEST、FORWARD、INCLUDE、ERROR,默認是REQUEST方式。
REQUEST:攔截直接請求方式;
?FORWARD:攔截請求轉發方式;
?INCLUDE:攔截請求包含方式;
ERROR:攔截錯誤轉發方式。
?
?