您好,登錄后才能下訂單哦!
小編給大家分享一下SpringBoot中過濾器Filter怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
在正式開始之前,有必要先簡單看一下什么是Filter(過濾器),以及這個(gè)有什么用
Filter,過濾器,屬于Servlet規(guī)范,并不是Spring獨(dú)有的。其作用從命名上也可以看出一二,攔截一個(gè)請(qǐng)求,做一些業(yè)務(wù)邏輯操作,然后可以決定請(qǐng)求是否可以繼續(xù)往下分發(fā),落到其他的Filter或者對(duì)應(yīng)的Servlet
簡單描述下一個(gè)http請(qǐng)求過來之后,一個(gè)Filter的工作流程:
首先進(jìn)入filter,執(zhí)行相關(guān)業(yè)務(wù)邏輯
若判定通行,則進(jìn)入Servlet邏輯,Servlet執(zhí)行完畢之后,又返回Filter,最后在返回給請(qǐng)求方
判定失敗,直接返回,不需要將請(qǐng)求發(fā)給Servlet
插播一句:上面這個(gè)過程,和AOP中的
@Around
環(huán)繞切面的作用差不多
接下來我們搭建一個(gè)web應(yīng)用方便后續(xù)的演示,借助SpringBoot搭建一個(gè)web應(yīng)用屬于比較簡單的活;
創(chuàng)建一個(gè)maven項(xiàng)目,pom文件如下
<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項(xiàng)目中,如果需要自定義一個(gè)Filter,并沒有什么特殊的地方,直接實(shí)現(xiàn)接口即可,比如下面一個(gè)輸出請(qǐng)求日志的攔截器
@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() { } }
實(shí)現(xiàn)一個(gè)自定義的Filter容易,一般有兩個(gè)步驟
實(shí)現(xiàn) Filter 接口
在doFilter
方法中添加業(yè)務(wù)邏輯,如果允許訪問繼續(xù),則執(zhí)行chain.doFilter(req, response);
; 不執(zhí)行上面這一句,則訪問到此為止
接下來的一個(gè)問題就是如何讓我們自定義的Filter生效,在SpringBoot項(xiàng)目中,有兩種常見的使用方式
@WebFilter
包裝Bean: FilterRegistrationBean
這個(gè)注解屬于Servlet3+,與Spring也沒有什么關(guān)系,所以問題來了,當(dāng)我在Filter上添加了這個(gè)注解之后,Spring怎么讓它生效呢?
配置文件中顯示使用注解 @ServletComponentScan
@ServletComponentScan @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
WebFilter常用屬性如下,其中urlPatterns
最為常用,表示這個(gè)filter適用于哪些url請(qǐng)求(默認(rèn)場(chǎng)景下全部請(qǐng)求都被攔截)
屬性名 | 類型 | 描述 |
---|---|---|
filterName | String | 指定過濾器的 name 屬性,等價(jià)于 <filter-name> |
value | String[] | 該屬性等價(jià)于 urlPatterns 屬性。但是兩者不應(yīng)該同時(shí)使用。 |
urlPatterns | String[] | 指定一組過濾器的 URL 匹配模式。等價(jià)于 <url-pattern> 標(biāo)簽。 |
servletNames | String[] | 指定過濾器將應(yīng)用于哪些 Servlet。取值是 @WebServlet 中的 name 屬性的取值,或者是 web.xml 中<servlet-name> 的取值。 |
dispatcherTypes | DispatcherType | 指定過濾器的轉(zhuǎn)發(fā)模式。具體取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。 |
initParams | WebInitParam[] | 指定一組過濾器初始化參數(shù),等價(jià)于 <init-param> 標(biāo)簽。 |
asyncSupported | boolean | 聲明過濾器是否支持異步操作模式,等價(jià)于 <async-supported> 標(biāo)簽。 |
description | String | 該過濾器的描述信息,等價(jià)于 <description> 標(biāo)簽。 |
displayName | String | 該過濾器的顯示名,通常配合工具使用,等價(jià)于 <display-name> 標(biāo)簽。 |
上面一種方式比較簡單,后面會(huì)說到有個(gè)小問題,指定Filter的優(yōu)先級(jí)比較麻煩,
下面是使用包裝bean注冊(cè)方式
@Bean public FilterRegistrationBean<OrderFilter> orderFilter() { FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>(); filter.setName("reqFilter"); filter.setFilter(new ReqFilter()); // 指定優(yōu)先級(jí) filter.setOrder(-1); return filter; }
上面整完,就可以開始測(cè)試使用過濾器了,在進(jìn)入實(shí)測(cè)環(huán)節(jié)之前,先來看兩個(gè)常見的問題
Filter作為Servelt的組件,怎么與SpringBoot中的Bean交互
多個(gè)Filter之間的優(yōu)先級(jí)怎么確定
如果有小伙伴使用SpringMVC + web.xml方式來定義Filter,就會(huì)發(fā)現(xiàn)自定義的Filter中無法通過@Autowired
方式來注入Spring的bean
我之前使用的是spring4 Servlet2+ ,存在上面的問題,如果有不同觀點(diǎn)請(qǐng)留言告訴我,感謝
SpringBoot中可以直接注入依賴的Bean,從上面的第二種注冊(cè)方式可以看到,Spring將Filter封裝成了一個(gè)Bean對(duì)象,因此可以直接注入依賴的Bean
下面定義一個(gè)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); // 測(cè)試,用header中的 tx-demo 來判斷是否為認(rèn)證的請(qǐng)求 HttpServletRequest req = (HttpServletRequest) request; String auth = req.getHeader("tx-demo"); if ("yihuihui".equals(auth)) { // 只有認(rèn)證的請(qǐng)求才允許訪問,請(qǐng)求頭中沒有這個(gè)時(shí),不執(zhí)行下面的的方法,則表示請(qǐng)求被過濾了 // 在測(cè)試優(yōu)先級(jí)時(shí)打開下面的注釋 // chain.doFilter(request, response); } else { chain.doFilter(request, response); } } @Override public void destroy() { } }
Filter的優(yōu)先級(jí)指定,通過我的實(shí)際測(cè)試,@Order
注解沒有用,繼承 Ordered
接口也沒有用,再不考慮web.xml的場(chǎng)景下,只能通過在注冊(cè)Bean的時(shí)候指定優(yōu)先級(jí)
實(shí)例如下,三個(gè)Filter,兩個(gè)通過@WebFilter
注解方式注冊(cè),一個(gè)通過FilterRegistrationBean
方式注冊(cè)
@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); } }
上面定義了三個(gè)Filter,我們主要驗(yàn)證下優(yōu)先級(jí),如果@Order
注解生效,那么執(zhí)行的先后順序應(yīng)該是
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
)
上面是測(cè)試時(shí)關(guān)鍵鏈路的斷點(diǎn)截圖,從數(shù)組中可以看出 AuthFilter
的優(yōu)先級(jí)大于ReqFilter
, 下面實(shí)際的輸出也說明了@Order
注解不能指定Filter的優(yōu)先級(jí)(不知道為什么網(wǎng)絡(luò)上有大量使用Order來指定Filer優(yōu)先級(jí)的文章!!!)
接下來我們的問題就是WebFilter
注解來注冊(cè)的Filter的優(yōu)先級(jí)是怎樣的呢,我們依然通過debug來看,關(guān)鍵代碼路徑為: org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
OrderFiler是我們手動(dòng)注冊(cè)并設(shè)置優(yōu)先級(jí)為-1
ReqFilter, AuthFilter通過 WebFillter方式注冊(cè),默認(rèn)優(yōu)先級(jí)為2147483647
,相同優(yōu)先級(jí)的情況下,根據(jù)名字先后順序來決定
本文主要介紹了過濾器Filter的使用方式,以及常見的兩個(gè)問題解答,文中內(nèi)容穿插了一點(diǎn)源碼的分析截圖,并未深入,如有興趣的同學(xué)可以根據(jù)文中提的幾個(gè)關(guān)鍵位置探索一番
下面簡單小結(jié)下文中內(nèi)容
自定義Filter的實(shí)現(xiàn)
實(shí)現(xiàn)Filter接口
doFilter方法中,顯示調(diào)用chain.doFilter(request, response);
表示請(qǐng)求繼續(xù);否則表示請(qǐng)求被過濾
注冊(cè)生效
@ServletComponentScan
自動(dòng)掃描帶有@WebFilter
注解的Filter
創(chuàng)建Bean: FilterRegistrationBean
來包裝自定義的Filter
在SpringBoot中Filter可以和一般的Bean一樣使用,直接通過Autowired
注入其依賴的Spring Bean對(duì)象
通過創(chuàng)建FilterRegistrationBean
的時(shí)候指定優(yōu)先級(jí),如下
@Bean public FilterRegistrationBean<OrderFilter> orderFilter() { FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>(); filter.setName("orderFilter"); filter.setFilter(new OrderFilter()); filter.setOrder(-1); return filter; }
此外格外注意, @WebFilter
聲明的Filter,優(yōu)先級(jí)為2147483647
(最低優(yōu)先級(jí))
@Order注解不能指定Filter優(yōu)先級(jí)
@Order注解不能指定Filter優(yōu)先級(jí)
@Order注解不能指定Filter優(yōu)先級(jí)
以上是“SpringBoot中過濾器Filter怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。