如何使用Filter
先看下filter接口。
package javax.servlet;
import java.io.IOException;
/**
* A filter is an object that performs filtering tasks on either the request to
* a resource (a servlet or static content), or on the response from a resource,
* or both. <br>
* <br>
* Filters perform filtering in the <code>doFilter</code> method. Every Filter
* has access to a FilterConfig object from which it can obtain its
* initialization parameters, a reference to the ServletContext which it can
* use, for example, to load resources needed for filtering tasks.
* <p>
* Filters are configured in the deployment descriptor of a web application
* <p>
* Examples that have been identified for this design are<br>
* 1) Authentication Filters <br>
* 2) Logging and Auditing Filters <br>
* 3) Image conversion Filters <br>
* 4) Data compression Filters <br>
* 5) Encryption Filters <br>
* 6) Tokenizing Filters <br>
* 7) Filters that trigger resource access events <br>
* 8) XSL/T filters <br>
* 9) Mime-type chain Filter <br>
*
* @since Servlet 2.3
*/
public interface Filter {
/**
* Called by the web container to indicate to a filter that it is being
* placed into service. The servlet container calls the init method exactly
* once after instantiating the filter. The init method must complete
* successfully before the filter is asked to do any filtering work.
* <p>
* The web container cannot place the filter into service if the init method
* either:
* <ul>
* <li>Throws a ServletException</li>
* <li>Does not return within a time period defined by the web
* container</li>
* </ul>
* The default implementation is a NO-OP.
*
* @param filterConfig The configuration information associated with the
* filter instance being initialised
*
* @throws ServletException if the initialisation fails
*/
public default void init(FilterConfig filterConfig) throws ServletException {}
/**
* The <code>doFilter</code> method of the Filter is called by the container
* each time a request/response pair is passed through the chain due to a
* client request for a resource at the end of the chain. The FilterChain
* passed in to this method allows the Filter to pass on the request and
* response to the next entity in the chain.
* <p>
* A typical implementation of this method would follow the following
* pattern:- <br>
* 1. Examine the request<br>
* 2. Optionally wrap the request object with a custom implementation to
* filter content or headers for input filtering <br>
* 3. Optionally wrap the response object with a custom implementation to
* filter content or headers for output filtering <br>
* 4. a) <strong>Either</strong> invoke the next entity in the chain using
* the FilterChain object (<code>chain.doFilter()</code>), <br>
* 4. b) <strong>or</strong> not pass on the request/response pair to the
* next entity in the filter chain to block the request processing<br>
* 5. Directly set headers on the response after invocation of the next
* entity in the filter chain.
*
* @param request The request to process
* @param response The response associated with the request
* @param chain Provides access to the next filter in the chain for this
* filter to pass the request and response to for further
* processing
*
* @throws IOException if an I/O error occurs during this filter's
* processing of the request
* @throws ServletException if the processing fails for any other reason
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
/**
* Called by the web container to indicate to a filter that it is being
* taken out of service. This method is only called once all threads within
* the filter's doFilter method have exited or after a timeout period has
* passed. After the web container calls this method, it will not call the
* doFilter method again on this instance of the filter. <br>
* <br>
*
* This method gives the filter an opportunity to clean up any resources
* that are being held (for example, memory, file handles, threads) and make
* sure that any persistent state is synchronized with the filter's current
* state in memory.
*
* The default implementation is a NO-OP.
*/
public default void destroy() {}
}
使用filter的方式很簡單,首先編寫一個類實現Filter接口,在doFilter中進行攔截操作,參數request和response由connector生成,層層傳遞過來,FilterChain就是當前filter所在的攔截鏈,filter的doFilter函數也是由FilterChain調用的。以下的代碼展示了如何修改request中body的字符集。
public class CharacterEncodingFilter implements Filter {
private FilterConfig filterConfig;
private String defaultCharset = "UTF-8";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String charset = this.filterConfig.getInitParameter("charset");
if(charset == null)
charset = defaultCharset;
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse rep = (HttpServletResponse) response;
req.setCharacterEncoding(charset);
rep.setCharacterEncoding(charset);
rep.setContentType("text/html;charset="+charset);
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
}
接着在web.xml中配置filter。
<filter>
<filter-name>FilterDemo2</filter-name>
<filter-class>nagi.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
request.setCharacterEncoding函數只能更改請求 body的文本編碼,不能改變請求網址的編碼。
Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader(). Otherwise, it has no effect.
Filter從生到死
FilterChain構造
當請求傳遞到servlet對應的StandardWrapperValve類時,如果當前的servlet可以被訪問,那么會分配一個servlet實例並構造一個ApplicationFilterChain來處理這次請求。
創建一個ApplicationFilterChain實例后,會設置對應的servlet,將當前servletContext中的FilterMap數組取出,如果匹配請求的dispatch模式和路徑,或者是匹配請求的dispatch模式和servlet的名稱,就會將FilterMap對應的ApplicationFilterConfig加入到ApplicationFilterChain中。FilterMap和ApplicationFilterConfig都存儲在servletContext中。
FilterChain使用
Filter由對應的FilterConfig獲得,ApplicationFilterChain的第一次DoFilter由StandardWrapperValve調用,每次調用DoFilter,ApplicationFilterChain中的index便會指向下一個FilterMap。如果沒有index>=FilterMap數組的長度,便會調用對應servlet類的service函數。
在這里除了第一次調用DoFilter由ApplicationFilterChain代碼執行,其余的DoFilter都需要由Filter調用。
FilterMap和FilterConfig的加載
當Tomcat啟動的時候,StandardHost下的StandardContext會調用初始化器的OnStartUp()函數,觸發ContextConfig的Start事件,接着回去調用Digester類解析web.xml文件,Digester類會通過反射調用WebXml.addFilter和WebXml.addFilterMapping函數,將FilterDef和filterMap添加到WebXml的實例中。合並完WebXml后,會檢驗每個filterMap的合法性,並將其添加到StandardContext的filterMap數組中。
如果配置都合法,那么StandardContext會調用filterStart函數,將StandardContext和filterDefs注冊到ApplicationFilterConfig中,並將其添加到filterConfigs Map中去。可以通過filterMap的name從filterConfig中獲得對應的filterDef。
FilterChain中Filter的順序
同一個web.xml的filter在FilterChain中的順序由filterMapping標簽的順序決定,因為代碼中生成FilterConfig,再添加到FilterChain的操作是遍歷FilterMapping數組完成的,FilterMapping數組中Filter的順序決定了FilterChain中Filter的調用順序。
addFilter處理的是以下xml標記
<filter>
<filter-name>FilterDemo2</filter-name>
<filter-class>nagi.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
addFilterMapping處理如下xml標記
<filter-mapping>
<filter-name>FilterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>