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實例化,然后通過如下配置文件,進行具體的配置:
@Configuration
public 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渲染了對應的視圖之后執行。這個方法的主要是用于進行資源清理。
來看一個具體的示例:
@Component
public 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當中:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
}
}
這里攔截所有的請求,執行對應的Controller之后,會看到打印如下信息:
Interceptor preHandle
Controller中處理業務邏輯
Interceptor postHandle
Interceptor 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的反射機制的,而過濾器是基于函數回調。
上面介紹了過濾器和攔截器的基本不同之處,這里再對上面的圖進一步細化,可得到下圖:

通過上圖,我們可以進一步看到攔截器和過濾器的方法在整個請求過程中所處的位置。
小結
通過上面的學習,想必大家已經掌握了過濾器和攔截器的基本使用。最后補充一下,什么時候適合使用過濾器,什么時候又適合使用攔截器呢?當需要過濾掉其中的部分信息,只留一部分時,就用過濾器;當需要對其流程進行更改,做相關的記錄時用攔截器。