HandlerInterceptor 實現請求預處理、後處理、完成回調,典型場景:登錄驗證、權限校驗、日誌記錄、性能監控、限流熔斷。本文系統講解兩大接口區別、三方法執行時機、鏈順序規則、XML/註解/程序化三種註冊方式、最佳實踐。
| 維度 | HandlerInterceptor | WebRequestInterceptor |
|---|---|---|
| 核心參數 | 原生 HttpServletRequest/Response | Spring 封裝 WebRequest (統一 Servlet/Portlet) |
| preHandle 返回值 | boolean (false 終止鏈) | void (無法終止,拋異常終止) |
| postHandle 參數 | ModelAndView (可修改視圖/模型) | ModelMap (可修改模型) |
| afterCompletion 參數 | Exception (可感知異常) | Exception (同) |
| 適用場景 | 通用 Web 應用 (推薦) | 跨 Servlet/Portlet、需統一 Request 封裝 |
public class AuthInterceptor implements HandlerInterceptor {
// 1. 前置處理:Controller 執行前
// 返回 false → 直接響應,後續 Interceptor + Controller 全不執行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 例:登錄驗證
if (request.getSession().getAttribute("user") == null) {
response.sendRedirect("/login");
return false; // 關鍵:攔截終止
}
// 例:放入請求屬性供後續使用
request.setAttribute("startTime", System.currentTimeMillis());
return true; // 放行
}
// 2. 後置處理:Controller 執行後、視圖渲染前
// 僅在 preHandle 返回 true 時調用
// 執行順序:與聲明順序相反 (後聲明先執行)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
// 例:統一添加頁面佈局數據
if (modelAndView != null) {
modelAndView.addObject("footer", "© 2024 MyApp");
}
// 例:性能監控
Long start = (Long) request.getAttribute("startTime");
if (start != null) {
long cost = System.currentTimeMillis() - start;
request.setAttribute("cost", cost);
}
}
// 3. 完成回調:視圖渲染後 (無論成功/異常)
// 僅在 preHandle 返回 true 時調用
// 主要用於資源清理、線程局部變量移除
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 例:清理 ThreadLocal
RequestContextHolder.remove();
// 例:異常記錄
if (ex != null) {
log.error("Request failed: " + request.getRequestURI(), ex);
}
}
}
假設聲明順序:InterceptorA → InterceptorB → InterceptorC
執行流程:
┌────────────────────────────────────────────────────────────────────┐
│ preHandle: A.preHandle → B.preHandle → C.preHandle │
│ (任一返回 false 終止,直接跳到對應 afterCompletion) │
├────────────────────────────────────────────────────────────────────┤
│ Controller: 執行目標 Handler 方法 │
├────────────────────────────────────────────────────────────────────┤
│ postHandle: C.postHandle → B.postHandle → A.postHandle │
│ (逆序!類似 Servlet Filter / AOP 環繞) │
├────────────────────────────────────────────────────────────────────┤
│ afterCompletion: C.afterCompletion → B.afterCompletion → A.afterCompletion │
│ (逆序,保證資源按獲取逆序釋放) │
└────────────────────────────────────────────────────────────────────┘
<!-- 1. 啟用 mvc 命名空間 -->
<beans xmlns:mvc="https://www.springframework.org/schema/mvc"
xsi:schemaLocation="https://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:interceptors>
<!-- 全局攔截:直接定義 bean,攔截所有請求 -->
<bean class="com.example.GlobalInterceptor"/>
<!-- 路徑精確攔截:mvc:interceptor + mvc:mapping -->
<mvc:interceptor>
<mvc:mapping path="/api/**"/>
<mvc:exclude-mapping path="/api/login"/> <!-- 排除登錄 -->
<bean class="com.example.ApiAuthInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/admin/**"/>
<bean class="com.example.AdminInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 全局攔截
registry.addInterceptor(new GlobalInterceptor())
.addPathPatterns("/**");
// API 驗證攔截
registry.addInterceptor(new ApiAuthInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/login", "/api/register");
// 管理後台攔截
registry.addInterceptor(new AdminInterceptor())
.addPathPatterns("/admin/**")
.order(1); // 顯式指定順序 (數字小優先)
}
}
@Component
@Order(1) // 順序控制
public class GlobalInterceptor implements HandlerInterceptor { ... }
// 需配置 WebMvcConfigurer 開啟自動註冊 (Spring Boot 2.0+ 默認開啟)
// 或手動在 WebConfig 中 registry.addInterceptor(globalInterceptor);
public class SessionPrepareInterceptor implements WebRequestInterceptor {
// 請求前:準備資源 (如 Hibernate Session、DB 連接)
@Override
public void preHandle(WebRequest request) {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// 存入請求作用域
request.setAttribute("hibernateSession", session, WebRequest.SCOPE_REQUEST);
request.setAttribute("transaction", tx, WebRequest.SCOPE_REQUEST);
}
// 請求後:可修改 ModelMap
@Override
public void postHandle(WebRequest request, ModelMap model) {
model.put("serverTime", new Date());
}
// 完成後:釋放資源
@Override
public void afterCompletion(WebRequest request, Exception ex) {
Session session = (Session) request.getAttribute("hibernateSession", WebRequest.SCOPE_REQUEST);
Transaction tx = (Transaction) request.getAttribute("transaction", WebRequest.SCOPE_REQUEST);
if (tx != null) {
if (ex != null) tx.rollback(); else tx.commit();
}
if (session != null) session.close();
}
}
SCOPE_REQUEST (0):僅當前請求。SCOPE_SESSION (1):會話級 (若環境支持隔離會話則隔離)。SCOPE_GLOBAL_SESSION (2):全局會話級 (Portlet 環境)。response.sendRedirect / response.getWriter().write,否則客戶端收空響應。afterCompletion 的 ex 參數僅在 Controller 拋異常且未被 @ExceptionHandler 捕獲時非 null。request.setAttribute / ThreadLocal (配合 afterCompletion 清理)。excludePathPatterns("/static/**", "/css/**", "/js/**") 或配置 mvc:resources。@Order / registry.order() 顯式指定,避免聲明順序依賴。preHandle 極輕量,避免 DB 查詢/複雜計算;需共享數據用緩存。Spring MVC 攔截器 = HandlerInterceptor (推薦) / WebRequestInterceptor + 三方法生命週期 + 逆序 postHandle/afterCompletion + 靈活路徑匹配 + 三種註冊方式。掌握preHandle 布爾值控制鏈終止、postHandle 修改 ModelAndView、afterCompletion 清理資源、順序顯式指定、線程安全無字段狀態,即可優雅實現登錄驗證、權限、日誌、監控等橫切邏輯。Spring Boot 時代首選 WebMvcConfigurer.addInterceptors 程序化配置,類型安全、可測試、易維護。