SpringBoot&Filter的詳解
過濾器實際上就是對web資源進行攔截,做一些處理後再交給下一個過濾器或servlet處理 通常都是用來攔截request進行處理的,也可以對返回的response進行攔截處理
應用場景
自動登入
統一設定編碼格式
訪問許可權控制
敏感字元過濾等
......
1、Filter的使用
要想使用filter,需要寫一個方法繼承Filter類,我們寫如下兩個自己的Filter類,首先是FirstFilter類,其中@Order裡邊的數字越小代表越先被該Filter過濾,@WebFilter
注意:
SpringBoot 實現 Filter 兩種方式:
自定義Filter通過FilterRegistrationBean 類來注入;(不推薦)
自定義Filter通過 實現介面 Filter方法,@WebFilter注入。
FirstFilter
@Order(1) @WebFilter(filterName="firstFilter", urlPatterns="/*") public class FirstFilter implements Filter { @Override public voidinit(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("first filter 1111111111111111111"); chain.doFilter(request, response); System.out.println("first filter 2222222222222222222"); } @Override public void destroy() { } }
SecondFilter
@Order(2) @WebFilter(filterName="secondFilter", urlPatterns="/*") public class SecondFilter 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("second filter 1============================="); System.out.println("before:" + response); chain.doFilter(request, response); System.out.println("after:" + response); System.out.println("second filter 2============================="); } @Override public void destroy() { } }
TestController
@RestController public class TestController { @GetMapping("/test1") public String test1() { System.out.println("method in controller"); return "test1"; } }
postman測試:
http://localhost:8080/test1
輸出結果:
first filter 1111111111111111111 second filter 1============================= before:org.apache.catalina.connector.ResponseFacade@1d2aafd8 method in controller after:org.apache.catalina.connector.ResponseFacade@1d2aafd8 second filter 2============================= first filter 2222222222222222222
過程:
總結:
我們可以看出程式碼執行的流程,首先請求被firstfilter截獲,打印出first filter 1,然後去執行chain.doFilter(request, response),這句話代表著請求會轉發給過濾器鏈上下一個物件,也就是secondfilter,所以打印出secondfilter裡的second filter 1,接下來再執行secondfilter裡的chain.dofilter()方法,請求再轉發給下一個物件,由於沒有其他的filter了,所以會轉發給controller,打印出了controller類中的method in controller,接下來再去記憶體棧裡呼叫secondfilter的print("second filter 2"),然後再去記憶體棧裡呼叫firstfilter的print("first filter 1")。所以如果在自己實現的Filter類的doFilter方法裡不加chain.doFilter(req, rep)是萬萬不行的,那樣會導致請求到了這個filter裡就不再往下走了,永遠進不了controller中。
我們也可以在print("before:" + response)和print("after:" + response)這兩個地方打上斷點,然後除錯一下,你會發現在before那裡的response裡是什麼都麼有的,而在after那裡的response裡則是已經有了test1字串,也就是說controller類test1方法的返回值已經新增進了response,所以如果你想對請求的response做一下過濾處理,那麼一定要在chain.doFilter(res, rep)之後寫你的邏輯。
2、@WebFilter註解說明
@WebFilter 用於將一個類宣告為過濾器,該註解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為過濾器。該註解具有下表給出的一些常用屬性 ( 以下所有屬性均為可選屬性,但是 value、urlPatterns、servletNames 三者必需至少包含一個,且 value 和 urlPatterns 不能共存,如果同時指定,通常忽略 value 的取值 )
@WebFilter 的常用屬性
屬性名 | 型別 | 說 明 |
---|---|---|
filterName |
String | 指定過濾器的 name 屬性,等價與 <filter-name> 標籤 |
value |
String[] | 該屬性等價於 <url-pattern> 標籤,但是兩者不應該同時使用 |
urlPatterns |
String[] | 指定一組過濾器的 URL 匹配模式。等價於 <url-pattern> 標籤。 |
servletNames |
String[] |
用於指定過濾的servlet。取值是 @WebServlet 中的 name 屬性的取值,或者是 web.xml 中 <servlet-name> 的取值。 |
dispatcherTypes |
DispatcherType[] | 指定過濾器的轉發模式。具體取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST |
initParams |
WebInitParam[] | 用於設定該過濾器類的一些初始化引數,等同於<init-param> 標籤 |
asyncSupported |
Boolean | 宣告過濾器是否支援非同步操作模式,等價於 <async supported> 標籤 |
description |
String | 對該過濾器的描述資訊,等價於 <description> 標籤 |
displayName |
String | 過濾器顯示名,通常配合工具使用,等同與<display-name> 標籤 |
3、責任鏈模式的實際應用
Filter和FilterChain都是怎麼用責任鏈模式實現的,自定義實現一下, 使用Filter模式來模擬
Filter介面
public interface Filter { /** * 過濾器 * @param request 請求物件 * @param response 返回物件 */ public void doFilter(Request request, Response response, FilterChain chain); }
FilterChain類
{ 過濾器鏈 }
/** * @Desc: TODO 過濾器鏈【執行責任鏈模式的主要成員】 */ public class FilterChain implements Filter { /** * 過濾器列表 */ private List<Filter> filters = new ArrayList<>(); int index = 0; /** * 新增過濾器 * @param filter */ public FilterChain addFilter(Filter filter) { filters.add(filter); return this; } @Override public void doFilter(Request request, Response response, FilterChain chain) { if(index == filters.size()) { /** * 真正處理請求(此時開始處理 controller業務邏輯) */ return; } Filter filter = filters.get(index); index++; filter.doFilter(request, response, chain); } }
Request類和Response類
public class Request { public String requestStr; }
public class Response { public String responseStr; }
實現類(業務中需要的過濾器,以下舉例)
EncodeFilter
{ 設定統一編碼 }
public class EncodeFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { request.requestStr = request.requestStr + " 設定編碼 UTF-8"; System.out.println("EncodeFilter request Str:" + request.requestStr); chain.doFilter(request, response, chain); response.responseStr = response.responseStr + "-------------設定編碼 UTF-8"; System.out.println("EncodeFilter response Str:" + response.responseStr); } }
VerifyParameterFilter
{ 引數校驗 }
public class VerifyParameterFilter implements Filter { @Override public void doFilter(Request request, Response response, FilterChain chain) { request.requestStr = request.requestStr + " 引數驗證 成功"; System.out.println("VerifyParameterFilter request str:" + request.requestStr); chain.doFilter(request, response, chain); response.responseStr = response.responseStr + "======================"; System.out.println("VerifyParameterFilter response str:" + response.responseStr); } }
測試
public class FilterTest { public static void main(String[] args) { String msg = "張三"; Request request = new Request(); request.requestStr = msg; Response response = new Response(); response.responseStr = "success *****"; FilterChain fc = new FilterChain(); fc.addFilter(new EncodeFilter()) .addFilter(new VerifyParameterFilter()) .doFilter(request, response, fc); } }
結果:
EncodeFilter request Str:張三 設定編碼 UTF-8 VerifyParameterFilter request str:張三 設定編碼 UTF-8 引數驗證 成功 VerifyParameterFilter response str:success *****====================== EncodeFilter response Str:success *****======================-------------設定編碼 UTF-8
總結
Filter是針對請求進行攔截、意在請求前進行一些過濾、許可權校驗、日誌記錄、統一編碼等
所有Filter統一在FilterChain中組成一個鏈條,然後呼叫也統一由FilterChain來協調,確保以鏈條的模式執行