Filter基本上能夠說存在全部的JavaWeb項目中,好比最基本的一個請求參數的編碼
CharacterEncodingFilter
,你們通常都會配置下,那麼filter是幹嗎的呢?java
本篇將主要集中在fitler的如下幾個知識點:web
Filter稱之爲過濾器,是用來作一些攔截的任務, 在Servlet接受請求以前,作一些事情,若是不知足限定,能夠拒絕進入Servletspring
從上面的圖,能夠看出一個Filter的工做流程:安全
一個http請求過來以後mvc
經過上面的流程,能夠推算使用場景:app
要使用一個Filter,一半須要兩步,實現Filter接口的自定義類,web.xml中對filter的定義curl
public interface Filter { public void init(FilterConfig filterConfig) throws ServletException; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; public void destroy(); }
主要就三個方法,從命名來看,async
init
方法destroy
方法doFilter
,也就是主要的業務邏輯所在了詳細case後面再說ide
接下來就是xml的配置了,和Servlet相似,每自定義一個,都須要在xml中加上一個配置(挺繁瑣的操做的)學習
<!-- 解決亂碼的問題 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <async-supported>true</async-supported> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
配置也比較簡單了,一個 <filter> 一個 <filter-mapping> 前者定義具體的Filter,後者表示這個Filter攔截的URL (看起來和Servlet的配置規則沒什麼兩樣)
咱們的實例,就拿大名鼎鼎的CharacterEncodingFilter
來講明,順帶膜拜下Spring的大神的優秀源碼
public class CharacterEncodingFilter extends OncePerRequestFilter { private String encoding; private boolean forceEncoding = false; public CharacterEncodingFilter() { } public CharacterEncodingFilter(String encoding) { this(encoding, false); } public CharacterEncodingFilter(String encoding, boolean forceEncoding) { Assert.hasLength(encoding, "Encoding must not be empty"); this.encoding = encoding; this.forceEncoding = forceEncoding; } public void setEncoding(String encoding) { this.encoding = encoding; } public void setForceEncoding(boolean forceEncoding) { this.forceEncoding = forceEncoding; } @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) { request.setCharacterEncoding(this.encoding); if (this.forceEncoding) { response.setCharacterEncoding(this.encoding); } } filterChain.doFilter(request, response); System.out.printl("servelt 執行完成,又返回filter"); } }
上面的實現比較簡單,主要將視線集中在 doFilterInternal
方法內部,若是要設置編碼參數,則直接修改 HttpServletRequest
, HttpServletResponse
兩個參數,操做完成以後,執行下面這一行
filterChain.doFilter(request, response);
注意
因此,若是你不但願繼續往下走,那麼就簡單了,不執行上面的那一行便可
問題一:看了上面的源碼,一個很明顯的問題就是,參數怎麼設置的?
仔細看上面的源碼,發現自定義Filter是繼承 org.springframework.web.filter.OncePerRequestFilter
而不是直接實現的 Filter 接口,並且方法內也沒有顯示的實現 init()
方法,全部很容易猜到是父類中實現了參數的初始化過程
具體的實現邏輯是在 org.springframework.web.filter.GenericFilterBean#init
中,一樣是Spring實現的,主要代碼撈出來
public final void init(FilterConfig filterConfig) throws ServletException { Assert.notNull(filterConfig, "FilterConfig must not be null"); if (logger.isDebugEnabled()) { logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'"); } this.filterConfig = filterConfig; // Set bean properties from init parameters. try { PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment)); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { String msg = "Failed to set bean properties on filter '" + filterConfig.getFilterName() + "': " + ex.getMessage(); logger.error(msg, ex); throw new NestedServletException(msg, ex); } // Let subclasses do whatever initialization they like. initFilterBean(); if (logger.isDebugEnabled()) { logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully"); } }
看上面一大串的代碼,到底幹了嘛? 簡單來說,就是獲取xml中配置的參數,而後填充到Filter對象中(對Srping而言,CharacterEncodingFilter就是一個bean),這個具體的邏輯和本篇關係不大,就直接跳過了
問題二:在Filter層中能夠獲取參數麼
從doFilter的方法簽名中看,既然有Request參數,那麼應該是能夠獲取到請求參數的,那麼實際驗證一下
先實現一個最最最簡單的Filter
public class TestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("in filter"); System.out.println("args: " + JSON.toJSONString(request.getParameterMap())); chain.doFilter(request, response); System.out.println("out filter"); } @Override public void destroy() { } }
開始測試
curl -d 'name=Hello&password=world' http://127.0.0.1:8088/123
輸出以下
in filter args: {"name":["Hello"],"password":["world"]} out filter
注意
在Filter中獲取參數時,最好不要直接使用獲取請求流的方式,若是獲取請求流,那麼Servlet就獲取不到請求參數了
問題三:多個filter的順序怎麼定
前面學習Servlet的時候,也有這個問題,一個URL被多個Servlet命中了,那麼前後順序是怎樣的呢?
那麼Filter呢,他們的區別仍是比較明顯的,不少Filter都是攔截全部的請求,即不少Filter的命中規則都是同樣的,那麼怎麼辦?
測試case以下,咱們定義三個Filter:
// ATestFilter @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("in ATestFilter"); chain.doFilter(request, response); } // TestFilter @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("in TestFilter"); chain.doFilter(request, response); } // ServletFilter @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("in ServletFilter"); chain.doFilter(request, response); }
對應的xml配置以下
<filter> <filter-name>servletFilter</filter-name> <filter-class>com.test.ServletFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>servletFilter</filter-name> <servlet-name>mvc-dispatcher</servlet-name> </filter-mapping> <filter> <filter-name>testFilter</filter-name> <filter-class>com.test.TestFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>testFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>atestFilter</filter-name> <filter-class>com.test.ATestFilter</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>atestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
輸出結果
in TestFilter in ATestFilter in ServletFilter
Filter 一般用於JavaWeb的過濾使用,經過doFilter方法中執行 chain.doFilter(request, response);
,進入下一個Filter或者Servlet執行邏輯,當執行完成以後,依然會回到Filter這一層,繼續走下去
針對上面的邏輯,Filter的常見應用場景有:
Filter的執行順序:
Filter的注意事項:
chain.doFilter(request, response)
, 最後把它放在finnal塊中,防止你在Filter中的代碼拋異常致使進入不到後續的邏輯盡信書則不如,已上內容,純屬一家之言,因本人能力通常,看法不全,若有問題,歡迎批評指正