web三大組件之一Filter,能夠說是不少小夥伴學習java web時最先接觸的知識點了,然而學得早不表明就用得多。基本上,若是不是讓你從0到1寫一個web應用(或者說即使從0到1寫一個web應用),在你的平常業務開發中不太可能碰到須要手寫Filter的場景java
本文將簡單介紹寫什麼是Filter,以及在SpringBoot中使用Filter的通常姿式與常見問題git
原文查看: SpringBoot系列教程web篇之過濾器Filter使用指南
在正式開始以前,有必要先簡單看一下什麼是Filter(過濾器),以及這個有什麼用github
Filter,過濾器,屬於Servlet規範,並非Spring獨有的。其做用從命名上也能夠看出一二,攔截一個請求,作一些業務邏輯操做,而後能夠決定請求是否能夠繼續往下分發,落到其餘的Filter或者對應的Servletweb
簡單描述下一個http請求過來以後,一個Filter的工做流程:spring
插播一句:上面這個過程,和AOP中的
@Around
環繞切面的做用差很少
接下來咱們搭建一個web應用方便後續的演示,藉助SpringBoot搭建一個web應用屬於比較簡單的活;apache
建立一個maven項目,pom文件以下json
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7</version> <relativePath/> <!-- lookup parent from update --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.45</version> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
在SpringBoot項目中,若是須要自定義一個Filter,並無什麼特殊的地方,直接實現接口便可,好比下面一個輸出請求日誌的攔截器數組
@Slf4j @WebFilter public class ReqFilter implements Filter { public ReqFilter() { System.out.println("init reqFilter"); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; log.info("url={}, params={}", req.getRequestURI(), JSON.toJSONString(req.getParameterMap())); chain.doFilter(req, response); } @Override public void destroy() { } }
實現一個自定義的Filter容易,通常有兩個步驟websocket
doFilter
方法中添加業務邏輯,若是容許訪問繼續,則執行chain.doFilter(req, response);
; 不執行上面這一句,則訪問到此爲止接下來的一個問題就是如何讓咱們自定義的Filter生效,在SpringBoot項目中,有兩種常見的使用方式網絡
FilterRegistrationBean
這個註解屬於Servlet3+,與Spring也沒有什麼關係,因此問題來了,當我在Filter上添加了這個註解以後,Spring怎麼讓它生效呢?
@ServletComponentScan
@ServletComponentScan @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
WebFilter經常使用屬性以下,其中urlPatterns
最爲經常使用,表示這個filter適用於哪些url請求(默認場景下所有請求都被攔截)
屬性名 | 類型 | 描述 |
---|---|---|
filterName | String | 指定過濾器的 name 屬性,等價於 <filter-name> |
value | String[] | 該屬性等價於 urlPatterns 屬性。可是二者不該該同時使用。 |
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> 標籤。 |
上面一種方式比較簡單,後面會說到有個小問題,指定Filter的優先級比較麻煩,
下面是使用包裝bean註冊方式
@Bean public FilterRegistrationBean<OrderFilter> orderFilter() { FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>(); filter.setName("reqFilter"); filter.setFilter(new ReqFilter()); // 指定優先級 filter.setOrder(-1); return filter; }
上面整完,就能夠開始測試使用過濾器了,在進入實測環節以前,先來看兩個常見的問題
若是有小夥伴使用SpringMVC + web.xml方式來定義Filter,就會發現自定義的Filter中沒法經過@Autowired
方式來注入Spring的bean
我以前使用的是spring4 Servlet2+ ,存在上面的問題,若是有不一樣觀點請留言告訴我,感謝
SpringBoot中能夠直接注入依賴的Bean,從上面的第二種註冊方式能夠看到,Spring將Filter封裝成了一個Bean對象,所以能夠直接注入依賴的Bean
下面定義一個AuthFilter
,依賴了自定義的DemoBean
@Data @Component public class DemoBean { private long time; public DemoBean() { time = System.currentTimeMillis(); } public void show() { System.out.println("demo bean!!! " + time); } } @Slf4j @WebFilter public class AuthFilter implements Filter { @Autowired private DemoBean demoBean; public AuthFilter() { System.out.println("init autFilter"); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { log.info("in auth filter! {}", demoBean); // 測試,用header中的 tx-demo 來判斷是否爲認證的請求 HttpServletRequest req = (HttpServletRequest) request; String auth = req.getHeader("tx-demo"); if ("yihuihui".equals(auth)) { // 只有認證的請求才容許訪問,請求頭中沒有這個時,不執行下面的的方法,則表示請求被過濾了 // 在測試優先級時打開下面的註釋 // chain.doFilter(request, response); } else { chain.doFilter(request, response); } } @Override public void destroy() { } }
Filter的優先級指定,經過個人實際測試,@Order
註解沒有用,繼承 Ordered
接口也沒有用,再不考慮web.xml的場景下,只能經過在註冊Bean的時候指定優先級
實例以下,三個Filter,兩個經過@WebFilter
註解方式註冊,一個經過FilterRegistrationBean
方式註冊
@Slf4j @Order(2) @WebFilter public class AuthFilter implements Filter, Ordered { ... } @Slf4j @Order(1) @WebFilter public class ReqFilter implements Filter, Ordered { ... } @Slf4j public class OrderFilter implements Filter { } @ServletComponentScan @SpringBootApplication public class Application { @Bean public FilterRegistrationBean<OrderFilter> orderFilter() { FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>(); filter.setName("orderFilter"); filter.setFilter(new OrderFilter()); filter.setOrder(-1); return filter; } public static void main(String[] args) { SpringApplication.run(Application.class); } }
上面定義了三個Filter,咱們主要驗證下優先級,若是@Order
註解生效,那麼執行的前後順序應該是
OrderFilter -> ReqFilter -> AuthFilter
若是不是上面的順序,那麼說明@Order
註解沒有用
@RestController public class IndexRest { @GetMapping(path = {"/", "index"}) public String hello(String name) { return "hello " + name; } }
(上文截圖源碼來自: org.apache.catalina.core.ApplicationFilterFactory#createFilterChain
)
上面是測試時關鍵鏈路的斷點截圖,從數組中能夠看出 AuthFilter
的優先級大於ReqFilter
, 下面實際的輸出也說明了@Order
註解不能指定Filter的優先級(不知道爲何網絡上有大量使用Order來指定Filer優先級的文章!!!)
接下來咱們的問題就是WebFilter
註解來註冊的Filter的優先級是怎樣的呢,咱們依然經過debug來看,關鍵代碼路徑爲: org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
2147483647
,相同優先級的狀況下,根據名字前後順序來決定本文主要介紹了過濾器Filter的使用方式,以及常見的兩個問題解答,文中內容穿插了一點源碼的分析截圖,並未深刻,若有興趣的同窗能夠根據文中提的幾個關鍵位置探索一番
下面簡單小結下文中內容
自定義Filter的實現
chain.doFilter(request, response);
表示請求繼續;不然表示請求被過濾註冊生效
@ServletComponentScan
自動掃描帶有@WebFilter
註解的FilterFilterRegistrationBean
來包裝自定義的Filter在SpringBoot中Filter能夠和通常的Bean同樣使用,直接經過Autowired
注入其依賴的Spring Bean對象
經過建立FilterRegistrationBean
的時候指定優先級,以下
@Bean public FilterRegistrationBean<OrderFilter> orderFilter() { FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>(); filter.setName("orderFilter"); filter.setFilter(new OrderFilter()); filter.setOrder(-1); return filter; }
此外格外注意, @WebFilter
聲明的Filter,優先級爲2147483647
(最低優先級)
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛