javascript
Spring Boot2.x-12 Spring Boot2.1.2中Filter和Interceptor 的使用
文章目錄
- Interceptor 攔截器
- 攔截器中方法的執行流程
- 傳統項目攔截器的配置
- Spring Boot2.1.2整合攔截器Interceptor 示例
- Step1 實現HandlerInterceptor接口編寫攔截器
- Step2 實現WebMvcConfigurer接口注冊攔截器
- Step3 驗證
- 多個攔截器的執行順序
- Filter 過濾器
- Spring Boot中整合過濾器Filter的兩種方式
- 方式一 FilterRegistrationBean注冊
- Step1 實現Filter接口 開發過濾器
- Step2 在配置類中注冊該過濾器
- Step3 啟動項目 測試
- 方式二 @WebServlet+ @ServletComponentScan注解的方式
- Step1 @WebFilter注解開發過濾器
- Step2 啟動類增加@ServletComponentScan注解
- Step3 啟動測試
- 小結
Interceptor 攔截器
所有的攔截器都需要實現 HandIerInterceptor 接口
HandlerInterceptor 源碼
public interface HandlerInterceptor {// 處理器執行前方法default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}// 處理器處理后方法default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {}// 處理器完成后方法default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {}}Interceptor依賴于web框架,我們經常在Spring MVC中用到該配置,在這個場景下Interceptor 就依賴于SpringMVC框架。
Interceptor 基于Java的反射機制,屬于AOP的一種運用
優點:
- 由于攔截器是基于web框架的調用,因此可以使用Spring的依賴注入進行一些業務操作,同時一個攔截器實例在一個controller生命周期之內可以多次調用
缺點:
- 只能對controller請求進行攔截,對其他的一些比如直接訪問靜態資源的請求則沒辦法進行攔截處理。
攔截器中方法的執行流程
- 請求到達 DispatcherServlet,DispatcherServlet 發送至 Interceptor ,執行 preHandle 方法,該方法會返回一個布爾值。如果為 false ,則結束所有流程:如果為 true , 則執行下一步。
- 請求達到 Controller,執行處理器邏輯,它包含控制器的功能 。
- 執行 postHandle 方法。
- 執行視圖解析和視圖渲染 。
- 執行 afterComp letion 方法。
傳統項目攔截器的配置
基于Spring MVC的項目 ,我們之前的案例配置攔截器的方式如下:
攔截器的開發還是一樣的沒有變化,那如何注冊和實例化攔截器呢? 上面是通過xml的方式來加載的 ,那基于Spring Boot的呢?
Spring Boot2.1.2整合攔截器Interceptor 示例
pom.xml僅需添加 spring-boot-starter-web即可
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>Step1 實現HandlerInterceptor接口編寫攔截器
繼承HandlerInterceptorAdapter也可以
package com.artisan.interceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;/*** 實現 Handlerlnterceptor接口,覆蓋其對應的方法即完成了攔截器的開發* * @author yangshangwei**/public class MyInterceptor implements HandlerInterceptor {/*** preHandle在執行Controller之前執行 * 返回true:繼續執行處理器邏輯,包含Controller的功能 * 返回false:中斷請求* * 處理器執行前方法*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("MyInterceptor-處理器執行前方法preHandle,返回true則不攔截后續的處理");return true;}/*** postHandle在請求執行完之后 渲染ModelAndView返回之前執行* * 處理器處理后方法*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);System.out.println("MyInterceptor-處理器處理后方法postHandle");}/*** afterCompletion在整個請求執行完畢后執行,無論是否發生異常都會執行* * 處理器完成后方法*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);System.out.println("MyInterceptor-處理器完成后方法afterCompletion");}}Step2 實現WebMvcConfigurer接口注冊攔截器
先說明下幾個可以用來注冊攔截器的類和接口
- WebMvcConfigurerAdapter: 2.0以后的版本已失效
- WebMvcConfigurationSupport: 不需要返回邏輯視圖,可以選擇繼承此類
- WebMvcConfigurer:返回邏輯視圖,可以選擇實現此方法,重寫addInterceptor方法
我們這里選擇使用實現WebMvcConfigurer接口
package com.artisan.conf;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import com.artisan.interceptor.MyInterceptor;/*** 實現 WebMvcConfigurer 接 口, 最后覆蓋其addInterceptors方法進行注冊攔截器* @author yangshangwei**/// 標注為配置類 @Configuration public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注冊攔截器到 Spring MVC 機制, 然后 它會返 回 一個攔截器注冊InterceptorRegistration regist = registry.addInterceptor(new MyInterceptor());// 指定攔截匹配模式,限制攔截器攔截請求regist.addPathPatterns("/artisan/interceptor/*");// 不攔截的路徑regist.excludePathPatterns("/artisan/interceptor/exclude/*");}}注意設置的規則,下面測試的時候需要對應上
Step3 驗證
package com.artisan.controller;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/artisan") public class ArtisanController {@GetMapping("/interceptor/test")public String testInterceptor() {System.out.println("執行處理器邏輯testInterceptor....");return "請觀察控制臺中攔截器的日志輸出";}@GetMapping("/interceptor/exclude/test")public String testExcludeInterceptor() {System.out.println("執行處理器邏輯testExcludeInterceptor....");return "exclude排除,不會被攔截器攔截";} }啟動Spring Boot工程,訪問
http://localhost:8080/artisan/interceptor/test 因為匹配到了攔截的規則,所以會被攔截
日志如下
訪問 http://localhost:8080/artisan/interceptor/exclude/test 因為匹配到了exclude的規則,所以不會被攔截。 日志中沒有攔截器信息的輸出
多個攔截器的執行順序
實際的開發中,多個攔截器是很正常的一個事兒,那么他們的執行順序又是怎樣的呢?
再新建兩個攔截器MyInterceptor2和MyInterceptor3,為了驗證下執行順序,不搞的太復雜,代碼和MyInterceptor一樣,僅僅方法中的輸出為了區分改成了對應的類名,如下所示
這3個攔截器的preHandle方法都返回true的情況下
先注冊下攔截器
啟動工程,訪問 http://localhost:8080/artisan/interceptor/test
日志輸出
上述結果是責任鏈模式的規則,對于處理器前方法采用先注冊先執行,而處理器后方法和完成方法則是先注冊后執行的規則
如果將第二個攔截器MyInterceptor2 的 preHandle 方法修改返回為 false 呢?
測試下,日志輸出如下
可以看出處理器前( preHandle )方法會執行,但是一旦返回 false ,則后續的攔截器、 處理器和所有攔截器的處理器后( postHandle ) 方法都不會被執行 。 完成方法 afterCompletion則不一樣,它只會執行返回 true 的攔截器的完成方法,而且順序是先注冊后執行 。
Filter 過濾器
在開發傳統的Spring項目時web.xml中配置的編碼過濾器不知道你還記不記得?
比如這篇很久前寫的這個基于Spring的SSM整合文章SSM-Spring+SpringMVC+MyBatis整合案例從0到1 中為了避免編碼不一致增加了編碼過濾器配置
既然是配置在web.xml中,那肯定是依賴于servlet容器.
優點:
- 在實現上Filter是基于函數回調,可以對幾乎所有請求進行過濾
缺點:
- 一個過濾器實例只能在容器初始化時調用一次 . 當容器第一次加載該過濾器時,init() 方法將被調用
使用場景:
- 比如設置編碼、過濾敏感詞匯、禁止瀏覽器緩存所有動態頁面、實現用戶自動登陸、實現URL級別的權限認證等等 ,具體案例參考Filter(過濾器)常見應用
傳統的JavaEE項目開發filter的主要2個步驟
- 實現Filter接口,并實現其doFilter方法。
- 在 web.xml 文件中使用<filter>和<filter-mapping>元素對編寫的filter類進行注冊,并設置它所能攔截的資源
- 可以開發編寫多個Filter,組成一個Filter鏈,根據Filter在web.xml文件中的注冊順序,決定先調用哪個Filter
Spring Boot中整合過濾器Filter的兩種方式
方式一 FilterRegistrationBean注冊
Step1 實現Filter接口 開發過濾器
package com.artisan.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.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public class HttpFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("HttpFilter init");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("HttpFilter doFilter begin");HttpServletRequest req =(HttpServletRequest) request;HttpServletResponse res =(HttpServletResponse) response;System.out.println("HttpFilter name:" + request.getParameter("name"));// 將請求轉發給過濾器鏈上下一個對象。這里的下一個指的是下一個filter,如果沒有filter那就是請求的資源。chain.doFilter(request, response);System.out.println("HttpFilter doFilter end");}@Overridepublic void destroy() {System.out.println("HttpFilter destroy");Filter.super.destroy();} }Step2 在配置類中注冊該過濾器
package com.artisan.conf;import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import com.artisan.filter.HttpFilter;@Configuration public class FilterConfig {@Beanpublic FilterRegistrationBean<HttpFilter> httpFilter(){FilterRegistrationBean<HttpFilter> filterRegistrationBean = new FilterRegistrationBean<HttpFilter>();// 設置filterfilterRegistrationBean.setFilter(new HttpFilter());// 攔截規則filterRegistrationBean.addUrlPatterns("/*");return filterRegistrationBean;}}Step3 啟動項目 測試
觀察啟動日志
然后訪問 http://localhost:8080/artisan/interceptor/test?name=artisan
方式二 @WebServlet+ @ServletComponentScan注解的方式
Step1 @WebFilter注解開發過濾器
@WebFilter 用于將一個類聲明為過濾器,該注解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署為過濾器
package com.artisan.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 javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;@WebFilter(filterName = "HttpFilter2", urlPatterns = "/*") public class HttpFilter2 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("HttpFilter2 init");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("HttpFilter2 doFilter begin");HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse res = (HttpServletResponse) response;System.out.println("HttpFilter2 name:" + request.getParameter("name"));// 將請求轉發給過濾器鏈上下一個對象。這里的下一個指的是下一個filter,如果沒有filter那就是請求的資源。chain.doFilter(request, response);System.out.println("HttpFilter2 doFilter end");}@Overridepublic void destroy() {System.out.println("HttpFilter2 destroy");Filter.super.destroy();} }Step2 啟動類增加@ServletComponentScan注解
在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通過 @WebServlet、@WebFilter、@WebListener 注解自動注冊
Step3 啟動測試
觀察啟動日志
訪問 http://localhost:8080/artisan/interceptor/test?name=artisan
注意下filter的執行在interceptor之前
小結
Filter的執行順序在Interceptor之前 。 攔截器(Interceptor)是基于Java的反射機制,而過濾器(Filter)是基于函數回調。從靈活性上說攔截器功能更強大些,Filter能做的事情,Interceptor都能做,而且可以在請求前,請求后執行,比較靈活。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Spring Boot2.x-12 Spring Boot2.1.2中Filter和Interceptor 的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot2.x-11 使用
- 下一篇: gradle idea java ssm