SpringBoot中filter的使用詳解及原理
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
? 作者?|??SUPERUSR
來源 |? urlify.cn/nIrqyu
66套java從入門到精通實(shí)戰(zhàn)課程分享
?首先還是老生常談,我先把SpringBoot中filter的使用示例寫出來,然后再解釋下代碼、說一下運(yùn)行的順序,最后講一下filter的原理(其實(shí)就是責(zé)任鏈設(shè)計(jì)模式,從馬士兵老師那里偷來的。。。)。
? ? ? ? 要想使用filter,需要寫一個(gè)方法繼承Filter類,我們寫如下兩個(gè)自己的Filter類,首先是FirstFilter類,其中@Order里邊的數(shù)字越小代表越先被該Filter過濾,@WebFilter代表這是個(gè)Filter類并把這個(gè)類注入到容器中:
package?com.example.executor_test.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.annotation.WebFilter;
?
import?org.springframework.core.annotation.Order;
?
@Order(1)
@WebFilter(filterName="firstFilter",?urlPatterns="/*")
public?class?FirstFilter?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("first?filter?1");
????????chain.doFilter(request,?response);
????????System.out.println("first?filter?2");
????}
?
????@Override
????public?void?destroy()?{
????????
????}
}
?然后是第二個(gè)Filter,SecondFilter類:
package?com.example.executor_test.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.annotation.WebFilter;
?
import?org.springframework.core.annotation.Order;
?
@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()?{
????????
????}
}
然后我們把Controller類也寫出來吧:
package?com.example.executor_test.controller;
?
import?java.text.DateFormat;
import?java.text.SimpleDateFormat;
import?java.util.Date;
?
import?javax.annotation.Resource;
?
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.web.bind.annotation.GetMapping;
import?org.springframework.web.bind.annotation.RestController;
?
import?com.example.executor_test.task.OldTask;
import?com.example.executor_test.task.OldTaskThread;
?
@RestController
public?class?TestController?{
????
????@GetMapping("/test1")
????public?String?test1()?{
????????System.out.println("method?in?controller");
????????return?"test1";
????}
?
}
? ? 最后是springboot的主方法入口,注意,由于我們使用注解注入的Filter,所以要在下邊這個(gè)Application類中加入@ServletComponentScan注解:
package?com.example.executor_test;
?
import?org.omg.CORBA.PRIVATE_MEMBER;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.boot.SpringApplication;
import?org.springframework.boot.autoconfigure.SpringBootApplication;
import?org.springframework.boot.web.servlet.ServletComponentScan;
import?org.springframework.context.ConfigurableApplicationContext;
?
import?com.example.executor_test.task.OldTaskThread;
?
@SpringBootApplication
@ServletComponentScan
public?class?ExecutorTestApplication?{
?
????public?static?void?main(String[]?args)?{????
????????ConfigurableApplicationContext?applicationContext?=?SpringApplication.run(ExecutorTestApplication.class,?args);
????}
}
? 首先我們先來看一下執(zhí)行結(jié)果,啟動后訪問127.0.0.1:8080/test1,在后臺中打印如下信息:

?
?我們可以看出代碼執(zhí)行的流程,首先請求被firstfilter截獲,打印出first filter 1,然后去執(zhí)行chain.doFilter(request, response),這句話代表著請求會轉(zhuǎn)發(fā)給過濾器鏈上下一個(gè)對象,也就是secondfilter,所以打印出secondfilter里的second filter 1,接下來再執(zhí)行secondfilter里的chain.dofilter()方法,請求再轉(zhuǎn)發(fā)給下一個(gè)對象,由于沒有其他的filter了,所以會轉(zhuǎn)發(fā)給controller,打印出了controller類中的method in controller,接下來再去內(nèi)存棧里調(diào)用secondfilter的print("second filter 2"),然后再去內(nèi)存棧里調(diào)用firstfilter的print("first filter 1")。所以如果在自己實(shí)現(xiàn)的Filter類的doFilter方法里不加chain.doFilter(req, rep)是萬萬不行的,那樣會導(dǎo)致請求到了這個(gè)filter里就不再往下走了,永遠(yuǎn)進(jìn)不了controller中。
? ? ? ? 我們也可以在print("before:" + response)和print("after:" + response)這兩個(gè)地方打上斷點(diǎn),然后調(diào)試一下,你會發(fā)現(xiàn)在before那里的response里是什么都么有的,而在after那里的response里則是已經(jīng)有了test1字符串,也就是說controller類test1方法的返回值已經(jīng)添加進(jìn)了response,所以如果你想對請求的response做一下過濾處理,那么一定要在chain.doFilter(res, rep)之后寫你的邏輯。
? ? ? ? 接下來講一下這個(gè)Filter和FilterChain都是怎么用責(zé)任鏈模式實(shí)現(xiàn)的,如果不太需要了解原理的話,往下的部分就可以不看了。。。好了,我們來模擬一下簡單的實(shí)現(xiàn)SpringBoot中的Filter接口和FilterChain類:
首先是我們自己寫的Filter接口,里邊就一個(gè)doFilter方法:
package?filterchain_pattern;
public?interface?Filter?{
public?void?doFilter(Request?request,?Response?response,?FilterChain?chain);
}
接下來是我們自己寫的FilterChain類:
package?filterchain_pattern;
?
?
?
import?java.util.ArrayList;
?
import?java.util.List;
?
?
?
public?class?FilterChain?implements?Filter?{
?
?
?
private?List?filters?=?new?ArrayList<>();
?
int?index?=?0;
?
?
?
public?FilterChain?addFilter(Filter?filter)?{
?
filters.add(filter);
?
return?this;
?
}
?
?
?
@Override
?
public?void?doFilter(Request?request,?Response?response,?FilterChain?chain)?{
?
?
?
if(index?==?filters.size())?{
?
return;
?
}
?
Filter?filter?=?filters.get(index);
?
index++;
?
filter.doFilter(request,?response,?chain);
?
}
?
?
?
}
接下來模擬Request類和Response類:
package?filterchain_pattern;
?
?
?
public?class?Request?{
?
?
?
public?String?requestStr;
?
?
?
}
package?filterchain_pattern;
?
?
?
public?class?Response?{
?
?
?
public?String?responseStr;
?
?
?
}
然后我們下一個(gè)Filter接口的實(shí)現(xiàn)類HTMLFilter類,該類會將requestStr中的<>替換成[],并給responseStr添加------------HTML response filter字符串,并在控制臺打印出來:
package?filterchain_pattern;
?
?
?
public?class?HTMLFilter?implements?Filter?{
?
?
?
@Override
?
public?void?doFilter(Request?request,?Response?response,?FilterChain?chain)?{
?
request.requestStr?=?request.requestStr.replace("<",?"[").replace(">",?"]")?+?"--------HTML?Request?Filter";
?
System.out.println("HTML?Filter?request?Str:"?+?request.requestStr);
?
chain.doFilter(request,?response,?chain);
?
response.responseStr?=?response.responseStr?+?"-------------HTML?response?filter";
?
System.out.println("HTML?Filter?response?Str:"?+?response.responseStr);
?
}
?
}
然后是另外一個(gè)Filter接口的實(shí)現(xiàn)類SensitiveFilter類, 該類會給requestStr添加一段字符串,給responseStr添加一段字符串,并在控制臺打印出來:
package?filterchain_pattern;
?
?
?
public?class?SensitiveFilter?implements?Filter?{
?
?
?
@Override
?
public?void?doFilter(Request?request,?Response?response,?FilterChain?chain)?{
?
request.requestStr?=?request.requestStr?+?"---------------Sensitive?request?Filter";
?
System.out.println("sensitiveFilter?request?str:"?+?request.requestStr);
?
chain.doFilter(request,?response,?chain);
?
response.responseStr?=?response.responseStr?+?"---------------------sensitive?response?filter";
?
System.out.println("sensitiveFilter?response?str:"?+?response.responseStr);
?
}
?
?
?
}
最后使我們的Main方法類:
package?filterchain_pattern;
?
?
?
public?class?MainTest?{
?
?
?
public?static?void?main(String[]?args)?{
?
?
?
String?msg?=?"testMsg";
?
Request?request?=?new?Request();
?
request.requestStr?=?msg;
?
Response?response?=?new?Response();
?
response.responseStr?=?"responseStr";
?
?
?
FilterChain?fc?=?new?FilterChain();
?
fc.addFilter(new?HTMLFilter()).addFilter(new?SensitiveFilter());
?
fc.doFilter(request,?response,?fc);
?
?
?
}
?
?
?
}
打印結(jié)果如下,這就是責(zé)任鏈模式的實(shí)際應(yīng)用了:

粉絲福利:實(shí)戰(zhàn)springboot+CAS單點(diǎn)登錄系統(tǒng)視頻教程免費(fèi)領(lǐng)取
???
?長按上方微信二維碼?2 秒 即可獲取資料
感謝點(diǎn)贊支持下哈?
