您好,登錄后才能下訂單哦!
這篇文章給大家介紹SpringBoot web中過濾器Filter的使用方法,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
這里介紹另外一種直接將 Filter 當(dāng)做 Spring 的 Bean 來使用的方式,并且在這種使用方式下,F(xiàn)ilter 的優(yōu)先級可以直接通過@Order
注解來指定;最后將從源碼的角度分析一下兩種不同的使用方式下,為什么@Order
注解一個生效,一個不生效
本篇博文的工程執(zhí)行的環(huán)境依然是SpringBoot2+
, 項目源碼可以在文章最后面 get
前面一篇博文,介紹了兩種使用姿勢,下面簡單介紹一下
WebFilter 注解
在 Filter 類上添加注解@WebFilter
;然后再項目中,顯示聲明@ServletComponentScan
,開啟 Servlet 的組件掃描
@WebFilter public class SelfFilter implements Filter { } @ServletComponentScan public class SelfAutoConf { }
FilterRegistrationBean
另外一種方式則是直接創(chuàng)建一個 Filter 的注冊 Bean,內(nèi)部持有 Filter 的實例;在 SpringBoot 中,初始化的是 Filter 的包裝 Bean 就是這個
@Bean public FilterRegistrationBean<OrderFilter> orderFilter() { FilterRegistrationBean<OrderFilter> filter = new FilterRegistrationBean<>(); filter.setName("orderFilter"); filter.setFilter(new SelfFilter()); filter.setOrder(-1); return filter; }
本篇將介紹另外一種方式,直接將 Filter 當(dāng)做普通的 Bean 對象來使用,也就是說,我們直接在 Filter 類上添加注解@Component
即可,然后 Spring 會將實現(xiàn) Filter 接口的 Bean 當(dāng)做過濾器來注冊
而且這種使用姿勢下,F(xiàn)ilter 的優(yōu)先級可以通過@Order
注解來指定;
設(shè)計一個 case,定義兩個 Filter(ReqFilter
和OrderFilter
), 當(dāng)不指定優(yōu)先級時,根據(jù)名字來,OrderFilter 優(yōu)先級會更高;我們主動設(shè)置下,希望ReqFilter
優(yōu)先級更高
@Order(1) @Component public class ReqFilter 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("req filter"); chain.doFilter(request, response); } @Override public void destroy() { } } @Order(10) @Component public class OrderFilter 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("order filter!"); chain.doFilter(request, response); } @Override public void destroy() { } }
上面兩個 Filter 直接當(dāng)做了 Bean 來寫入,我們寫一個簡單的 rest 服務(wù)來測試一下
@RestController public class IndexRest { @GetMapping(path = {"/", "index"}) public String hello(String name) { return "hello " + name; } } @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
請求之后輸出結(jié)果如下, ReqFilter 優(yōu)先執(zhí)行了
當(dāng)我們直接將 Filter 當(dāng)做 Spring Bean 來使用時,@Order
注解來指定 Filter 的優(yōu)先級沒有問題;但是前面一篇博文中演示的@WebFilter
注解的方式,則并不會生效
這兩種方式的區(qū)別是什么?
@Order
注解到底有什么用,該怎么用
首先我們分析一下將 Filter 當(dāng)做 Spring bean 的使用方式,我們的目標(biāo)放在 Filter 的注冊邏輯上
第一步將目標(biāo)放在: org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
下面的邏輯中包括了 ServeltContext 的初始化,而我們的 Filter 則可以看成是屬于 Servlet 的 Bean
private void selfInitialize(ServletContext servletContext) throws ServletException { prepareWebApplicationContext(servletContext); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes( beanFactory); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, getServletContext()); existingScopes.restore(); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, getServletContext()); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
注意上面代碼中的 for 循環(huán),在執(zhí)行getServletContextInitializerBeans()
的時候,F(xiàn)ilter 就已經(jīng)注冊完畢,所以我們需要再深入進(jìn)去
將目標(biāo)集中在org.springframework.boot.web.servlet.ServletContextInitializerBeans#ServletContextInitializerBeans
public ServletContextInitializerBeans(ListableBeanFactory beanFactory) { this.initializers = new LinkedMultiValueMap<>(); addServletContextInitializerBeans(beanFactory); addAdaptableBeans(beanFactory); List<ServletContextInitializer> sortedInitializers = this.initializers.values() .stream() .flatMap((value) -> value.stream() .sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); this.sortedList = Collections.unmodifiableList(sortedInitializers); }
上面有兩行代碼比較突出,下面單獨撈出來了,需要我們重點關(guān)注
addServletContextInitializerBeans(beanFactory); addAdaptableBeans(beanFactory);
通過斷點進(jìn)來,發(fā)現(xiàn)第一個方法只是注冊了dispatcherServletRegistration
;接下來重點看第二個
@SuppressWarnings("unchecked") private void addAdaptableBeans(ListableBeanFactory beanFactory) { MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory); addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig)); addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter()); for (Class<?> listenerType : ServletListenerRegistrationBean .getSupportedTypes()) { addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType, new ServletListenerRegistrationBeanAdapter()); } }
從上面調(diào)用的方法命名就可以看出,我們的 Filter 注冊就在addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
上面的截圖就比較核心了,在創(chuàng)建FilterRegistrationBean
的時候,根據(jù) Filter 的順序來指定最終的優(yōu)先級
然后再回到構(gòu)造方法中,根據(jù) order 進(jìn)行排序, 最終確定 Filter 的優(yōu)先級
接下來我們看一下 WebFilter 方式為什么不生效,在根據(jù)我的項目源碼進(jìn)行測試的時候,請將需要修改一下自定義的 Filter,將類上的@WebFilter
注解打開,@Component
注解刪除,并且打開 Application 類上的ServletComponentScan
我們這里 debug 的路徑和上面的差別不大,重點關(guān)注下面ServletContextInitializerBeans
的構(gòu)造方法上面
當(dāng)我們深入addServletContextInitializerBeans(beanFactory);
這一行進(jìn)去 debug 的時候,會發(fā)現(xiàn)我們自定義的 Filter 是在這里面完成初始化的;而前面的使用方式,則是在addAdapterBeans()
方法中初始化的,如下圖
在getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)
的調(diào)用中就返回了我們自定義的 Bean,也就是說我們自定義的 Filter 被認(rèn)為是ServletContextInitializer
的類型了
然后我們換個目標(biāo),看一下 ReqFilter 在注冊的時候是怎樣的
關(guān)鍵代碼: org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
(因為 bean 很多,所以我們可以加上條件斷點)
通過斷點調(diào)試,可以知道我們的自定義 Filter 是通過
WebFilterHandler
類掃描注冊的, 對這一塊管興趣的可以深入看一下org.springframework.boot.web.servlet.ServletComponentRegisteringPostProcessor#scanPackage
上面只是聲明了 Bean 的注冊信息,但是還沒有具體的實例化,接下來我們回到前面的進(jìn)程,看一下 Filter 的實例過程
private <T> List<Entry<String, T>> getOrderedBeansOfType( ListableBeanFactory beanFactory, Class<T> type, Set<?> excludes) { Comparator<Entry<String, T>> comparator = (o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()); String[] names = beanFactory.getBeanNamesForType(type, true, false); Map<String, T> map = new LinkedHashMap<>(); for (String name : names) { if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) { T bean = beanFactory.getBean(name, type); if (!excludes.contains(bean)) { map.put(name, bean); } } } List<Entry<String, T>> beans = new ArrayList<>(); beans.addAll(map.entrySet()); beans.sort(comparator); return beans; }
注意我們的 Filter 實例在T bean = beanFactory.getBean(name, type);
通過這種方式獲取的 Filter 實例,并不會將 ReqFilter 類上的 Order 注解的值,來更新FilterRegistrationBean
的 order 屬性,所以這個注解不會生效
最后我們再看一下,通過 WebFilter 的方式,容器類不會存在ReqFilter.class
類型的 Bean, 這個與前面的方式不同
主要介紹了另外一種 Filter 的使用姿勢,將 Filter 當(dāng)做普通的 Spring Bean 對象進(jìn)行注冊,這種場景下,可以直接使用@Order
注解來指定 Filter 的優(yōu)先級
但是,這種方式下,我們的 Filter 的很多基本屬性不太好設(shè)置,一個方案是參考 SpringBoot 提供的一些 Fitler 的寫法,在 Filter 內(nèi)部來實現(xiàn)相關(guān)邏輯
關(guān)于SpringBoot web中過濾器Filter的使用方法就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。