詳解Spring中的攔截器與過濾器
在 SpringBoot 的 Web 項目開發(fā)中,如果想實現(xiàn)攔截、過濾的功能,大概會有三種做法:Filter 過濾器、Interceptor 攔截器、AOP 切面編程,而我們今天要討論的是 Filter 與 Interceptor 的做法及它們之間的區(qū)別。
Filter 過濾器
Filter 是 Servlet 中用于攔截請求、過濾請求的一個接口。在以前,我們通常會使用 Filter 來攔截請求設置請求的字符集、判斷用戶是否登陸、校驗權限等等。
其工作原理和核心配置文件 web.xml
息息相關,在配置文件中我們會配置過濾器的名稱,以及它過濾的 URL 規(guī)則。配置好后,符合過濾規(guī)則的請求就會先來到過濾器這里執(zhí)行 Filter 中的邏輯,以及判斷是否能進行下一步的流轉(zhuǎn)。
雖然使用原生的 Servlet 開發(fā)的時代大概率已經(jīng)過去,但是 Servlet 卻是 Web 開發(fā)基礎中的基礎,所以 Filter 接口也是能適用于 SpringBoot 項目的。
Filter 方法簡單介紹
public interface Filter { //Servlet容器(如Tomcat)在初始化這個Filter時調(diào)用,一般用于初始化一些資源 public default void init(FilterConfig filterConfig) throws ServletException {} //這個方法是具體執(zhí)行過濾器邏輯的方法 //另外chain變量是過濾器鏈,可以使用這個變量來決定這個請求是否可以向下流轉(zhuǎn) public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; //Servlet容器(如Tomcat)在關閉前會銷毀Filter,一般用于資源的釋放 public default void destroy() {} }
SpringBoot 中添加 Filter
SpringBoot 項目中添加 Filter 的步驟主要包括 Filter 定義與注冊,添加的方式有 3 種,下面一一做展示
方式一:使用 @WebFilter
注解 + @ServletComponentScan
注解
// 過濾器 package com.example.demo.filter.one; @WebFilter(filterName = "filter-one", urlPatterns = "/bad/*") public class FilterOne implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //執(zhí)行Filter邏輯 System.out.println("這是Filter過濾器1號"); //讓請求繼續(xù)進入Filter鏈的下一個節(jié)點 chain.doFilter(request, response); } } // 啟動類 @SpringBootApplication @ServletComponentScan(basePackages = "com.example.demo.filter.one") public class SpringBootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringBootDemoApplication.class, args); } }
方式二:使用 FilterRegistrationBean
來注冊一般過濾器
// 過濾器 public class FilterTwo implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //執(zhí)行Filter邏輯 System.out.println("這是Filter過濾器2號"); //讓請求繼續(xù)進入Filter鏈的下一個節(jié)點 chain.doFilter(request, response); } } // 配置類 @Configuration public class FilterConfiguration { @Bean public FilterRegistrationBean<FilterTwo> filterRegistrationBean() { FilterTwo filterTwo = new FilterTwo(); FilterRegistrationBean<FilterTwo> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(filterTwo); //設置過濾器名、過濾規(guī)則 filterRegistrationBean.setName("filter-two"); filterRegistrationBean.addUrlPatterns("/bad/*"); return filterRegistrationBean; } }
方式三:使用 DelegatingFilterProxyRegistrationBean
注冊已被 Spring 管理的過濾器
// 過濾器 @Component public class FilterThree implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //執(zhí)行Filter邏輯 System.out.println("這是Filter過濾器3號"); //讓請求繼續(xù)進入Filter鏈的下一個節(jié)點 chain.doFilter(request, response); } } // 配置類 @Configuration public class FilterConfiguration { @Bean public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean() { DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean = new DelegatingFilterProxyRegistrationBean("filterThree"); delegatingFilterProxyRegistrationBean.setName("filter-three"); delegatingFilterProxyRegistrationBean.addUrlPatterns("/bad/*"); return delegatingFilterProxyRegistrationBean; } }
Filter 原理
簡單介紹一下三種做法的 Filter 注冊原理
方式一,SpringBoot 在啟動時,ServletComponentScanRegistrar
類實現(xiàn)了 ImportBeanDefinitionRegistrar
接口,負責把 @ServletComponentScan
中的包路徑傳遞給 ServletComponentRegisteringPostProcessor
類,ServletComponentRegisteringPostProcessor
實現(xiàn)了 BeanFactoryPostProcessor
接口,在調(diào)用 postProcessBeanFactory()
方法時,使用 WebServletHandler
、WebFilterHandler
、WebListenerHandler
一個個對比,符合條件就調(diào)用 doHandle()
方法來把 Filter 作為 FilterRegistrationBean
類型的 Bean 注冊到 Spring IoC 容器中。
方式二和方式三差異不大
相同點,無論是 FilterRegistrationBean
還是 DelegatingFilterProxyRegistrationBean
,他們都是實現(xiàn)了 ServletContextInitializer
接口的,在調(diào)用 onStartup()
方法時,抽象基類 AbstractFilterRegistrationBean
會調(diào)用 addRegistration()
方法,這個方法就是根據(jù)兩個子類中返回的 Filter ,添加到 Spring IoC 容器中。
不同點,DelegatingFilterProxyRegistrationBean
通過傳入的 targetBeanName 名字,在 Spring IoC 容器中查找該 Fillter 類型的 Bean,并通過 DelegatingFilterProxy
生成基于這個 Bean 的代理 Filter 對象;而 FilterRegistrationBean
則是直接設置一個 Filter ,因此這個 Filter 可以由 Spring IoC 容器管理,也可不用管理。如果一個 Filter 被聲明為一個 Bean,而不通過 DelegatingFilterProxyRegistrationBean
添加到 Spring IoC 容器中,那么這個過濾器是無法添加過濾規(guī)則的,全局適用。
Filter 在請求中的工作流程
在一次請求里,F(xiàn)ilter 不是獨立工作,而是以 FilterChain 過濾鏈的形式來進行過濾,每次請求都根據(jù) URL 的匹配規(guī)則來找到符合規(guī)則的 Filter ,組裝成一條過濾鏈,請求經(jīng)過過濾鏈后才能到達 DispatcherServlet
這個 ApplicationFilterChain
在整個過濾器的工作鏈路中是一個核心角色,在 createFilterChain()
方法中,會按順序地添加符合規(guī)則的過濾器,組建成一條過濾器鏈交給 StandardWrapperValve
,在調(diào)用過濾器邏輯時,直接拿這條過濾器鏈來做過濾,過濾器中維護了過濾器的順序,接下來的邏輯就是各個 Filter
的過濾邏輯。執(zhí)行完各個過濾器后,如果這個請求都通過了過濾,那么最終會來到 DispatcherServlet
中。
HandlerInterceptor 攔截器
攔截器是 Spring 中的內(nèi)容,它依賴于 Spring 容器,能從 Spring 容器中獲取其他 Bean;攔截器提供了更加細顆粒度的攔截功能,更能體現(xiàn) AOP 思想。
HandlerInterceptor 方法簡單介紹
public interface HandlerInterceptor { //在請求被處理前(到達Controller前)進行處理,如果返回false,那么請求不往下進行 default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } //在請求被處理后(執(zhí)行完Controller邏輯后)進行處理 default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } //在頁面渲染結(jié)束后執(zhí)行,一般用于資源釋放 default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
SpringBoot 中添加 HandlerInterceptor
與 Filter 類似,添加 HandlerInterceptor 的步驟也分為兩步,定義與注冊
// 攔截器 @Component public class HandlerInterceptorOne implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("請求到Controller前-執(zhí)行 HandlerInterceptor 邏輯"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("Controller執(zhí)行完后-執(zhí)行 HandlerInterceptor 邏輯"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("返回視圖前-執(zhí)行 HandlerInterceptor 邏輯"); } } // 配置類 @Configuration public class InterceptorConfiguration implements WebMvcConfigurer { @Autowired private HandlerInterceptorOne handlerInterceptorOne; @Override public void addInterceptors(InterceptorRegistry registry) { //這冊攔截器 registry.addInterceptor(handlerInterceptorOne) //設置攔截的路徑 .addPathPatterns("/bad/*") //設置不攔截的路徑(排除這些路徑) .excludePathPatterns("/bad/test"); } }
HandlerInterceptor 原理
HandlerInterceptor
的工作與 Filter
差別不大,先往容器里注冊攔截器,當請求來到 DispatcherServlet
時,調(diào)用 getHandler()
方法根據(jù)請求的 URL 從容器中取出 URL 符合攔截規(guī)則的攔截器,組裝成一條攔截器鏈 HandlerExecutionChain
。然后 DispatcherServlet
按照 preHandle
-> handle(Controller)
-> postHandle
-> afterCompletion
的順序往下執(zhí)行。
總結(jié)
雖然兩者名字上、功能上都頗為相似,但他們還是有部分區(qū)別的:
1.從執(zhí)行順序上看:
Filter 是 Servlet 容器接收到請求后,但是在調(diào)用 Servlet 被調(diào)用執(zhí)行前執(zhí)行的;而 Interceptor 是 Servlet 被調(diào)用后,在請求到達 Controller 前執(zhí)行的
2.從攔截粒度來看:Filter 只能對 request、response 進行攔截;Interceptor 不僅可以對 request、response 進行操作,也可以對 handler、modelAndView 進行操作,具備了對 SpringMVC 組件的操作能力
3.從依賴從屬來看:Filter 依賴于 Servlet 容器;而 Interceptor 不依賴于 Servlet,依賴于 Spring 框架
綜上所述,在基于 SpringBoot 的項目開發(fā)中,如果有需要對請求攔截處理的場景,F(xiàn)ilter 和 HandlerInterceptor兩者之間,優(yōu)先選擇 HandlerInterceptor
以上就是詳解Spring中的攔截器與過濾器的詳細內(nèi)容,更多關于Spring攔截器過濾器的資料請關注腳本之家其它相關文章!
相關文章
springboot讀取nacos配置文件的實現(xiàn)
SpringBoot注冊服務到Nacos上,由Nacos來做服務的管理,本文主要介紹了springboot讀取nacos配置文件的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05Java源碼解析之HashMap的put、resize方法詳解
這篇文章主要介紹了Java源碼解析之HashMap的put、resize方法詳解,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有很大的幫助,需要的朋友可以參考下2021-04-04SpringBoot中的配置類(@Configuration)
這篇文章主要介紹了SpringBoot中的配置類(@Configuration),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06Java實現(xiàn)微信公眾號自定義菜單的創(chuàng)建方法示例
這篇文章主要介紹了Java實現(xiàn)微信公眾號自定義菜單的創(chuàng)建方法,結(jié)合實例形式分析了java創(chuàng)建微信公眾號自定義菜單的具體步驟、實現(xiàn)方法及相關操作注意事項,需要的朋友可以參考下2019-10-10