Spring Boot攔截器(Interceptor)與過濾器(Filter)詳細教程
目錄
- 概述
- 什么是攔截器(Interceptor)?
- 什么是過濾器(Filter)?
- 兩者的核心區別
- 使用場景
- 攔截器的典型應用
- 過濾器的典型應用
- 實現步驟
- 攔截器的創建與配置
- 過濾器的創建與配置
- 代碼示例
- 自定義攔截器
- 自定義過濾器
- 執行順序與流程
- 過濾器、攔截器、Controller的執行順序
- 可視化流程圖
- 常見問題與解決方案
- 總結
1. 概述
1.1 什么是攔截器(Interceptor)?
攔截器是 Spring MVC 框架的組件,基于 AOP(面向切面編程) 實現。它允許在請求處理的不同階段(如Controller方法執行前后)插入自定義邏輯。
1.2 什么是過濾器(Filter)?
過濾器是 Java Servlet規范 定義的組件,作用于所有進入容器的請求(如Tomcat)。它可以在請求到達Servlet前或響應返回客戶端前進行預處理和后處理。
1.3 核心區別
特性 | 攔截器(Interceptor) | 過濾器(Filter) |
---|---|---|
所屬框架 | Spring MVC | Servlet API |
作用范圍 | 僅Spring MVC管理的請求 | 所有請求(包括靜態資源) |
依賴 | 依賴Spring容器 | 依賴Servlet容器(如Tomcat) |
執行時機 | Controller方法前后 | Servlet處理前后 |
獲取Bean | 支持(通過Spring上下文) | 不支持(需通過其他方式注入) |
2. 使用場景
2.1 攔截器的典型應用
- 日志記錄:記錄請求參數、響應時間。
- 權限驗證:檢查用戶是否登錄或擁有權限。
- 事務管理:在Controller方法前后開啟/提交事務。
- 性能監控:統計接口耗時。
2.2 過濾器的典型應用
- 全局字符編碼:統一設置請求/響應的編碼(如UTF-8)。
- 跨域處理:添加CORS響應頭。
- XSS防御:過濾請求參數中的惡意腳本。
- 請求壓縮:對響應內容進行GZIP壓縮。
3. 實現步驟
3.1 創建攔截器
步驟:
- 實現
HandlerInterceptor
接口,重寫以下方法:preHandle()
:在Controller方法執行前調用。postHandle()
:在Controller方法執行后、視圖渲染前調用。afterCompletion()
:在請求完成后調用(視圖渲染后)。
- 注冊攔截器到Spring MVC配置。
代碼示例:
public class AuthInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 檢查用戶是否登錄if (request.getSession().getAttribute("user") == null) {response.sendRedirect("/login");return false; // 中斷請求}return true;}
}
注冊攔截器:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/static/**");}
}
注冊多個攔截器:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 第一個攔截器:日志(優先級高)registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**") // 攔截所有路徑.excludePathPatterns("/static/**"); // 排除靜態資源// 第二個攔截器:權限(優先級低)registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/api/**"); // 僅攔截/api路徑}
}
關鍵配置選項
配置方法 | 說明 |
---|---|
addPathPatterns("/api") | 指定攔截的路徑(支持Ant風格) |
excludePathPatterns("/login") | 排除特定路徑 |
order(1) | 顯式設置順序(默認按注冊順序) |
若要手動指定順序,可添加:
registry.addInterceptor(new LogInterceptor()).order(1);
registry.addInterceptor(new AuthInterceptor()).order(2);
3.2 創建過濾器
步驟:
- 實現
javax.servlet.Filter
接口,重寫doFilter
方法。 - 注冊過濾器到Servlet容器(通過注解或配置類)。
代碼示例:
@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("請求開始: " + ((HttpServletRequest) request).getRequestURI());chain.doFilter(request, response); // 繼續執行后續過濾器或ServletSystem.out.println("請求結束");}
}
注冊過濾器(若未使用@WebFilter):
@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<LoggingFilter> loggingFilter() {FilterRegistrationBean<LoggingFilter> bean = new FilterRegistrationBean<>();bean.setFilter(new LoggingFilter());bean.addUrlPatterns("/*");bean.setOrder(1); // 設置執行順序return bean;}
}
注意: 確保主類添加 @ServletComponentScan
以啟用 @WebFilter
注解。
4. 執行順序與流程
4.1 執行順序
- 過濾器(FilterChain) → 2. 攔截器(preHandle) → 3. Controller方法 → 4. 攔截器(postHandle) → 5. 視圖渲染 → 6. 攔截器(afterCompletion) → 7. 過濾器后續處理
4.2 流程圖
客戶端 → Filter.doFilter() → Interceptor.preHandle()→ Controller → Interceptor.postHandle()→ 視圖渲染 → Interceptor.afterCompletion()→ Filter.doFilter()后續處理 → 客戶端
5. 常見問題與解決方案
Q1:如何控制多個攔截器/過濾器的執行順序?
- 攔截器:通過
registry.addInterceptor()
的順序決定。 - 過濾器:通過
FilterRegistrationBean.setOrder()
設置優先級(值越小越先執行)。
Q2:攔截器中如何獲取Spring管理的Bean?
直接從Spring容器注入:
public class AuthInterceptor implements HandlerInterceptor {@Autowiredprivate UserService userService; // 直接注入
}
Q3:過濾器中如何修改請求參數?
通過自定義 HttpServletRequestWrapper
:
public class ModifyRequestWrapper extends HttpServletRequestWrapper {// 重寫getParameter等方法以修改參數
}// 在Filter中替換Request對象
chain.doFilter(new ModifyRequestWrapper(request), response);
Q4:攔截器和過濾器執行時出現異常如何處理?
- 攔截器:在
afterCompletion
中處理異常。 - 過濾器:使用
try-catch
包裹chain.doFilter()
。
Q5:如何讓某個攔截器全局生效?
使用 addPathPatterns("/**")
:
registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
Q6:如何跳過特定攔截器的執行?
在 preHandle
中返回 false
:
@Override
public boolean preHandle(...) {if (跳過條件) {return false; // 后續攔截器和Controller不會執行}return true;
}
Q7:攔截器之間如何共享數據?
通過 request.setAttribute
傳遞:
// 在第一個攔截器中存儲數據
request.setAttribute("key", "value");// 在后續攔截器中獲取
String value = (String) request.getAttribute("key");
6. 總結
-
選擇攔截器還是過濾器?
- 需要訪問Spring上下文或Controller信息 → 攔截器。
- 需處理所有請求(包括靜態資源) → 過濾器。
-
最佳實踐:
- 優先使用攔截器處理業務相關邏輯。
- 使用過濾器處理底層Servlet容器的任務(如編碼、壓縮)。