在 Spring Boot 項目中,我們經常會遇到需要在請求處理前后執行一些通用邏輯的場景,比如記錄日志、權限校驗、全局異常處理等。此時,我們通常會面臨兩種選擇:過濾器(Filter) 和 攔截器(Interceptor)。
雖然兩者都能實現類似的功能,但它們在實現原理、使用場景、執行時機等方面有著本質的區別。本文將帶你深入剖析這兩者的差異,并通過實戰案例,讓你徹底搞懂何時該用過濾器,何時該用攔截器。
一、核心概念:從根上理解它們的區別
1.1 過濾器(Filter)
- 規范層級:屬于 Java Servlet 規范 的一部分,由 Servlet 容器(如 Tomcat)直接管理。
- 作用范圍:對整個 Web 應用生效,包括靜態資源(如 HTML、CSS、JS 等)。
- 觸發時機:在請求進入 DispatcherServlet 之前,以及響應返回客戶端之后。
- 依賴關系:不依賴 Spring 容器,可以脫離 Spring 獨立使用。
1.2 攔截器(Interceptor)
- 規范層級:Spring MVC 框架提供的機制,由 Spring 容器管理。
- 作用范圍:僅對 Spring MVC 控制器(Controller) 的請求生效,默認不攔截靜態資源。
- 觸發時機:在請求進入 DispatcherServlet 之后,到達 Controller 之前,以及 Controller 處理完請求之后。
- 依賴關系:深度集成 Spring 容器,可以方便地使用 Spring 的依賴注入(DI)和 AOP 功能。
特性對比 | 過濾器(Filter) | 攔截器(Interceptor) |
---|---|---|
規范層級 | Java Servlet 規范 | Spring MVC 框架 |
作用范圍 | 所有請求(包括靜態資源) | 僅 Controller 請求 |
觸發時機 | DispatcherServlet 前后 | Controller 前后 |
依賴容器 | Servlet 容器 | Spring 容器 |
能否修改請求/響應 | 可以直接修改 | 不能直接修改,但可操作 Model 和 View |
二、執行流程:一個圖看懂它們的調用順序
為了更直觀地理解兩者的執行順序,我們來看一張經典的流程圖:
HTTP Request↓
Filter Chain(doFilter)↓
DispatcherServlet↓
Interceptor.preHandle↓
Controller Method↓
Interceptor.postHandle↓
View Rendering(如有)↓
Interceptor.afterCompletion↓
Filter Chain(返回響應)
從這個流程可以看出:
- 過濾器 是最外層的“大門”,所有請求都必須經過它。
- 攔截器 是內層的“小門”,只對進入 Spring MVC 的請求生效。
三、實戰演練:代碼說話最真實
3.1 過濾器(Filter)實戰:記錄請求耗時
@Component
public class LogFilter implements Filter {private static final Logger logger = LoggerFactory.getLogger(LogFilter.class);@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {long startTime = System.currentTimeMillis();HttpServletRequest httpRequest = (HttpServletRequest) request;logger.info("請求開始,URI: {}", httpRequest.getRequestURI());chain.doFilter(request, response); // 放行請求long duration = System.currentTimeMillis() - startTime;logger.info("請求結束,耗時: {}ms", duration);}
}
3.2 攔截器(Interceptor)實戰:登錄權限校驗
@Component
public class AuthInterceptor implements HandlerInterceptor {@Autowiredprivate AuthService authService; // 可以注入Spring Bean@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("Authorization");if (!authService.isValidToken(token)) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);return false; // 攔截請求}return true; // 放行請求}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {logger.info("Controller 方法執行完畢,準備渲染視圖");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {logger.info("請求處理完成,資源清理");}
}
3.3 配置攔截器到Spring容器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate AuthInterceptor authInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authInterceptor).addPathPatterns("/api/**") // 攔截所有 /api 開頭的請求.excludePathPatterns("/api/login"); // 排除登錄接口}
}
四、選型指南:什么時候用過濾器,什么時候用攔截器?
場景需求 | 推薦方案 |
---|---|
需要處理靜態資源(如HTML、CSS、JS) | 過濾器(Filter) |
需要最早攔截請求(如全局跨域處理) | 過濾器(Filter) |
需要訪問Spring Bean(如Service、Repository) | 攔截器(Interceptor) |
需要精確控制Controller方法的執行前后 | 攔截器(Interceptor) |
需要修改請求和響應的內容(如壓縮、編碼) | 過濾器(Filter) |
五、常見誤區與最佳實踐
5.1 常見誤區
-
誤區1:攔截器可以處理靜態資源
實際上,攔截器默認不會攔截靜態資源,除非你手動配置了addResourceHandler
。 -
誤區2:過濾器可以直接使用Spring Bean
過濾器由Servlet容器管理,默認無法使用Spring的依賴注入。可以通過DelegatingFilterProxy
橋接,但配置較復雜。 -
誤區3:攔截器可以修改請求參數
攔截器無法直接修改請求參數,如需修改,需使用過濾器配合HttpServletRequestWrapper
。
5.2 最佳實踐
- 優先使用攔截器:除非必須處理靜態資源或需要最早攔截,否則優先使用攔截器,因為它更貼近業務邏輯,且易于測試。
- 避免復雜邏輯:無論是過濾器還是攔截器,都應避免復雜的業務邏輯,以免影響性能。
- 合理配置順序:如果同時使用多個過濾器或攔截器,務必注意它們的執行順序,避免邏輯沖突。
六、總結:一句話記住它們的區別
過濾器(Filter)是 Servlet 的“大門”,攔截器(Interceptor)是 Spring MVC 的“小門”。
- Filter:更早、更底層、更通用,但離業務邏輯較遠。
- Interceptor:更晚、更上層、更靈活,更貼近業務邏輯。