過濾器
Servlet過濾器可以對Servlet、JSP和HTML文件過濾。
過濾器在實際開發中用得較多,是屬於較重點的內容。
Servlet過濾器的概念
Servlet過濾器是在Java Servlet規范2.3中定義的,它能夠對Servlet容器的請求和響應對象進行檢查和修改。
Servlet過濾器本身並不生成請求和響應對象,它只提供過濾作用。
Servlet過濾器能夠在Servlet被調用之前檢查Request對象,修改Request Header和Request內容。
在Servlet被調用之后檢查Response對象,修改Response Header和Response內容。
Servlet過濾器負責過濾的Web組件可以是Servlet、JSP或HTML文件。
Servlet過濾器的過濾過程
過濾器的處理過程是一個鏈式的過程(FilterChain),即多個過濾器組成一個鏈,依次處理,最后交給過濾器之后的資源。
其中鏈式過濾過程中也可以直接給出響應,即返回,而不是向后傳遞。
Filter接口
所有的Servlet過濾器類都必須實現javax.servlet.Filter接口。
這個接口含有3個過濾器類必須實現的方法:
init(FilterConfig):這是Servlet過濾器的初始化方法,Servlet容器創建Servlet過濾器實例后將調用這個方法。
在這個方法中可以讀取web.xml文件中Servlet過濾器的初始化參數。
比如web.xml中聲明:
<filter> <filter-name>MyFilter1</filter-name> <filter-class>com.mengdd.filter.MyFilter1</filter-class> <init-param> <param-name>hello</param-name> <param-value>world</param-value> </init-param> <init-param> <param-name>name</param-name> <param-value>zhang</param-value> </init-param> </filter>
在MyFilter1中:
@Override public void init(FilterConfig filterConfig) throws ServletException { String paramValue1 = filterConfig.getInitParameter("hello"); String paramValue2 = filterConfig.getInitParameter("name"); ServletContext context = filterConfig.getServletContext(); }
注意:一旦一個過濾器啟動失敗,會導致整個Web應用啟動失敗。
doFilter(ServletRequest, ServletResponse, FilterChain):這個方法完成實際的過濾操作。
當客戶請求訪問與過濾器關聯的URL時,Servlet容器將先調用過濾器的doFilter方法。
FilterChain參數用於訪問后續過濾器。
在這個方法中調用chain.doFilter()方法,用於調用過濾器鏈中后續過濾器的doFilter()方法,假如沒有后續過濾器,那么就把客戶請求傳給相應的Web組件。
如果在這個方法中沒有調用chain.doFilter()方法,客戶請求不會到達所訪問的Web組件。
destroy():Servlet容器在銷毀過濾器實例前調用該方法,在這個方法中可以釋放Servlet過濾器占用的資源。
過濾器的例子
比如用戶登錄之后,將信息傳入session中,之后的其他頁面入口可能需要檢測是否存在session,以避免直接在地址欄輸入地址訪問頁面而造成的session中沒有用戶信息的情況,如果在每個Servlet或JSP前面都要判斷session中是否有相應屬性,會有很多的代碼重復,用過濾器可以很好地解決這個問題,如果不含登錄信息,即轉向到用戶登錄頁面。

package com.mengdd.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class LoginFilter implements Filter { @Override public void destroy() { // 由Web容器調用 // 在這里進行資源釋放 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter invoked!"); HttpServletRequest req = (HttpServletRequest) request; HttpSession session = req.getSession();// 獲取請求的session,如果沒有,會創建一個新的session // 對不需要進行過濾的請求進行事先篩選操作 String requestURI = req.getRequestURI(); if (requestURI.endsWith("login.jsp") || requestURI.endsWith("LoginServlet")) { chain.doFilter(request, response); return; } if (null == session.getAttribute("username")) { // session中沒有用戶名屬性,說明是新的session,之前沒有登錄 // 用過濾器完成了整個Web應用中未登錄情況的處理 ((HttpServletResponse) response).sendRedirect("login.jsp"); // 這里注意,由於本filter配置的<url-pattern>/*</url-pattern>是針對所有地址的 // 所以請求發送到login.jsp頁面也會需要先經過此過濾器,造成重定向的遞歸重復調用 // 所以需要對請求的URI進行判斷 } else { // 按照過濾鏈繼續往下走 chain.doFilter(request, response); } } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("Filter init"); // 過濾器是非常特殊的一個Servlet,它會在容器啟動的時候得到初始化 // 過濾器的啟動失敗會導致Web應用啟動失敗 } }
過濾器的配置和Servlet的配置相似,只不過URL配置的是要過濾請求的URL:
<!-- filter一般配置在所有的Servlet上面 --> <filter> <filter-name>LoginFilter</filter-name> <filter-class>com.mengdd.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- /*表示所有地址,即所有請求都會被送到這個過濾器 -->
可以做很多練習例子,比如可以創建一個NoteFilter過濾器,它可以拒絕列在黑名單上的客戶訪問留言簿。(黑名單通過過濾器參數設置)。
也可以利用Filter進行一些關鍵詞修改替換的工作。
串聯過濾器的例子
幾點說明:
1.串聯過濾器的順序是按照在web.xml中的配置順序為准的。

<filter> <filter-name>MyFilter1</filter-name> <filter-class>com.mengdd.filter.MyFilter1</filter-class> <init-param> <param-name>hello</param-name> <param-value>world</param-value> </init-param> <init-param> <param-name>name</param-name> <param-value>zhang</param-value> </init-param> </filter> <filter> <filter-name>MyFilter2</filter-name> <filter-class>com.mengdd.filter.MyFilter2</filter-class> </filter> <filter-mapping> <filter-name>MyFilter1</filter-name> <url-pattern>/InfoServlet</url-pattern> </filter-mapping> <filter-mapping> <filter-name>MyFilter2</filter-name> <url-pattern>/InfoServlet</url-pattern> </filter-mapping>
2.在Servlet過濾器中能訪問application范圍內的共享數據:先調用FilterConfig的getServletContext()方法獲得ServletContext,再調用ServletContext的getAttribute()方法來獲得application范圍內的共享數據。

package com.mengdd.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class MyFilter1 implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter1 --> doFilter invoked!"); System.out.println("MyFilter1 --> before chain.doFilter()"); chain.doFilter(request, response); System.out.println("MyFilter1 --> after chain.doFilter()"); } @Override public void init(FilterConfig filterConfig) throws ServletException { String paramValue1 = filterConfig.getInitParameter("hello"); String paramValue2 = filterConfig.getInitParameter("name"); ServletContext context = filterConfig.getServletContext(); } }

package com.mengdd.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class MyFilter2 implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter2 --> doFilter invoked!"); System.out.println("MyFilter2 --> before chain.doFilter()"); chain.doFilter(request, response); System.out.println("MyFilter2 --> after chain.doFilter()"); } @Override public void init(FilterConfig filterConfig) throws ServletException { } }
參考資料
聖思園張龍老師Java Web視頻教程。