Spring的攔截器與Servlet的Filter有相似之處,比如二者都是AOP程式設計思想的體現,都能實現許可權檢查、日誌記錄等。但它們之間又有不少區別,很多朋友工作多年,可能還沒有深刻的瞭解它們的具體使用以及它們之間的區別。本文帶大家全面瞭解一下它們的使用、實現機制以及區別。
過濾器(Filter)的詳解及使用過濾器(Filter)屬於Servlet的範疇,可以認為是Servlet的一種“加強版”,透過實現javax.servlet.Filter介面來實現功能。主要用於對使用者請求進行預處理,是個典型的處理鏈。通常使用場景:檢查使用者授權、記錄日誌資訊、解碼、過濾字元編碼等。
基本工作原理:配置完過濾器及需要攔截的請求,當請求到來時,透過過濾器提供的方法可以對請求或響應(Request、Response)統一處理。比如,可判斷使用者是否登入,是否擁有請求的訪問許可權等。在Web應用啟動時,過濾器僅會被初始化一次,便可處理後續請求,只有Web應用停止或重新部署時才能銷燬。
使用Filter完整的流程是:Filter對使用者請求進行“預處理”,接著將請求交給Servlet進處理並生成響應,最後Filter再對伺服器響應進行“後處理”。
上述流程具體到類的處理就是:
1、Filter在ServletRequest到達Servlet之前,攔截客戶的ServletRequest;
2、根據需要檢查ServletRequest,也可修改ServletRequest頭和資料;
3、在ServletResponse到達客戶端之前,攔截ServletResponse;
4、根據需要檢查HttpServletResponse,也可修改HttpServletResponse頭和資料。
建立Filter必須實現javax.servlet.Filter介面,該介面定義了3個方法:
void init(FilterConfig filterConfig):容器啟動初始化Filter時會被呼叫,整個生命週期只會被呼叫一次。可用於完成Filter的初始化。void doFilter(ServletRequest request, ServletResponse response,FilterChain chain):實現過濾功能,就是透過該方法對每個請求增加額外的處理。透過其引數FilterChain呼叫下一個過濾器。void destroy():用於Filter銷燬前,完成某些資源的回收。其中,doFilter方法便是實現對使用者請求進行預處理(ServletRequest request)和對伺服器響應進行後處理(ServletResponse response)的方法。預處理和後處理的分界線為是否呼叫了chain.doFilter()。在執行該方法之前,是對使用者請求進行預處理,在執行該方法之後,是對伺服器響應進行後處理。
下面以具體的實現程式碼來展示一下:
public class LogFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("Filter 初始化"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter 預處理"); filterChain.doFilter(servletRequest, servletResponse); System.out.println("Filter 後處理"); } @Override public void destroy() { System.out.println("容器銷燬"); }}
關於Filter的使用在普通的Web專案中可在web.xml中配置:
<filter> <filter-name>encodingFilter</filter-name> <filter-class>com.secbro2.learn.filter.LogFilter</filter-class> <async-supported>true</async-supported> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param></filter><filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
如果是SpringBoot專案,首先使用@Component將LogFilter例項化,然後透過如下配置檔案,進行具體的配置:
@Configurationpublic class FilterConfig { @Resource private LogFilter logFilter; @Bean public FilterRegistrationBean<Filter> registerAuthFilter() { FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(); registration.setFilter(logFilter); registration.addUrlPatterns("/*"); registration.setName("authFilter"); // 值越小,Filter越靠前 registration.setOrder(1); return registration; }}
定義一個Contoller,然後依次執行啟動專案、訪問Controller、關閉專案,列印的日誌資訊依次為:
Filter 初始化---以上為啟動專案時列印---Filter 預處理Controller中處理業務邏輯Filter 後處理---以上為訪問Controller時列印---容器銷燬---以上為關閉服務時列印---
攔截器(Interceptor)的詳解及使用
攔截器,在AOP(Aspect-Oriented Programming)中用於某個方法或欄位被訪問之前進行攔截,然後在其之前或之後加入某些操作。攔截器作為動態攔截Action呼叫的物件,它提供了一種機制使開發者可以在Action執行前後定義可執行的程式碼,也可以在Action執行前阻止其執行。
攔截器將Action共用的行為獨立出來,在Action執行前後執行。常見的應用場景比如許可權管理、日誌服務等。
在Spring MVC當中要使用攔截器需要實現org.springframework.web.servlet.HandlerInterceptor介面,該介面定義瞭如下三個方法:
(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,會在請求處理之前被呼叫。SpringMVC中的Interceptor是鏈式呼叫的,可以存在多個Interceptor。Interceptor的呼叫會依據宣告順序依次執行,最先執行的都是preHandle方法,可在該方法中進行一些前置(預)處理,也可進行判斷來決定是否要繼續執行。當返回為false 時,表示請求結束,後續的Interceptor和Controller都不會再執行;當返回值為true時,會繼續呼叫下一個Interceptor的preHandle方法,執行完最後一個Interceptor後會呼叫當前請求的Controller方法。
(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,會在Controller方法呼叫之後,DispatcherServlet進行渲染檢視之前被呼叫,所以可以對Controller處理之後的ModelAndView物件進行操作。postHandle方法被呼叫的方向跟preHandle是相反的,先宣告的Interceptor的postHandle方法反而會後執行。
(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,會在整個請求結束之後被呼叫,也就是在DispatcherServlet渲染了對應的檢視之後執行。這個方法的主要是用於進行資源清理。
來看一個具體的示例:
@Componentpublic class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("Interceptor preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { System.out.println("Interceptor postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("Interceptor afterCompletion"); }}
對應LoginInterceptor需新增到Spring MVC當中:
@Configurationpublic class WebConfig implements WebMvcConfigurer { @Resource private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); }}
這裡攔截所有的請求,執行對應的Controller之後,會看到列印如下資訊:
Interceptor preHandleController中處理業務邏輯Interceptor postHandleInterceptor afterCompletion
很明顯可以看到,當一個請求過來之後,會先後執行preHandle方法、Controller中的業務、postHandle方法和afterCompletion方法。
過濾器與攔截器的區別經過上面的學習,我們已經大概瞭解了過濾器和攔截器的基本使用和功能,想必已經感覺到它們之間的一些區別了。先看一張圖,可以更加明顯的看出過濾器和攔截器在使用過程中所處的位置和使用的時機。
彙總一下就是:
1、使用範圍與規範不同:Filter是Servlet規範中定義的,只能用於Web程式中,依賴於Servlet容器。攔截器是Spring的元件,可用於Web程式、Application、Swing等程式,不依賴Servlet容器。
2、使用資源不同:攔截器可以使用Spring裡的任何資源、物件,例如Service物件、資料來源、事務管理等,透過IOC注入到攔截器即可;而Filter則不能。
3、作用範圍不同:Filter在只在Servlet前後起作用。而攔截器能夠深入到方法前後、異常丟擲前後,對Action請求其作用,可以訪問Action上下文、值棧裡的物件等,具有更大的彈性。因此,在Spring框架的過程中,要優先使用攔截器。而濾器則可以對幾乎所有的請求起作用。
4、實現機制不同:攔截器是基於java的反射機制的,而過濾器是基於函式回撥。
上面介紹了過濾器和攔截器的基本不同之處,這裡再對上面的圖進一步細化,可得到下圖:
透過上圖,我們可以進一步看到攔截器和過濾器的方法在整個請求過程中所處的位置。
小結透過上面的學習,想必大家已經掌握了過濾器和攔截器的基本使用。最後補充一下,什麼時候適合使用過濾器,什麼時候又適合使用攔截器呢?當需要過濾掉其中的部分資訊,只留一部分時,就用過濾器;當需要對其流程進行更改,做相關的記錄時用攔截器。