首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring MVC 攔截器完整指南:HandlerInterceptor 三方法、WebRequestInterceptor 異同、鏈執行順序、配置註冊與最佳實踐

     編輯:Java 綜合教學

前言:攔截器是 Spring MVC 橫切關注點的利器

HandlerInterceptor 實現請求預處理、後處理、完成回調,典型場景:登錄驗證、權限校驗、日誌記錄、性能監控、限流熔斷。本文系統講解兩大接口區別、三方法執行時機、鏈順序規則、XML/註解/程序化三種註冊方式、最佳實踐。

一、HandlerInterceptor vs WebRequestInterceptor

維度HandlerInterceptorWebRequestInterceptor
核心參數原生 HttpServletRequest/ResponseSpring 封裝 WebRequest (統一 Servlet/Portlet)
preHandle 返回值boolean (false 終止鏈)void (無法終止,拋異常終止)
postHandle 參數ModelAndView (可修改視圖/模型)ModelMap (可修改模型)
afterCompletion 參數Exception (可感知異常)Exception (同)
適用場景通用 Web 應用 (推薦)跨 Servlet/Portlet、需統一 Request 封裝

二、HandlerInterceptor 三大方法詳解

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. XML 配置 (傳統)

<!-- 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>

2. @Configuration + WebMvcConfigurer (Spring Boot 推薦)

@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); // 顯式指定順序 (數字小優先)
    }
}

3. @Component + @Order (組件掃描自動註冊)

@Component
@Order(1) // 順序控制
public class GlobalInterceptor implements HandlerInterceptor { ... }

// 需配置 WebMvcConfigurer 開啟自動註冊 (Spring Boot 2.0+ 默認開啟)
// 或手動在 WebConfig 中 registry.addInterceptor(globalInterceptor);

五、WebRequestInterceptor 實現 (備選)

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();
    }
}

WebRequest 作用域常量

  • SCOPE_REQUEST (0):僅當前請求。
  • SCOPE_SESSION (1):會話級 (若環境支持隔離會話則隔離)。
  • SCOPE_GLOBAL_SESSION (2):全局會話級 (Portlet 環境)。

六、最佳實踐與避坑

  1. preHandle 返回 false 必須手動響應:response.sendRedirect / response.getWriter().write,否則客戶端收空響應。
  2. 異常處理:afterCompletion 的 ex 參數僅在 Controller 拋異常且未被 @ExceptionHandler 捕獲時非 null。
  3. 線程安全:Interceptor 單例,**字段不可存請求狀態**,用 request.setAttribute / ThreadLocal (配合 afterCompletion 清理)。
  4. 靜態資源放行:excludePathPatterns("/static/**", "/css/**", "/js/**") 或配置 mvc:resources。
  5. 順序控制:@Order / registry.order() 顯式指定,避免聲明順序依賴。
  6. 性能:preHandle 極輕量,避免 DB 查詢/複雜計算;需共享數據用緩存。

七、總結

Spring MVC 攔截器 = HandlerInterceptor (推薦) / WebRequestInterceptor + 三方法生命週期 + 逆序 postHandle/afterCompletion + 靈活路徑匹配 + 三種註冊方式。掌握preHandle 布爾值控制鏈終止、postHandle 修改 ModelAndView、afterCompletion 清理資源、順序顯式指定、線程安全無字段狀態,即可優雅實現登錄驗證、權限、日誌、監控等橫切邏輯。Spring Boot 時代首選 WebMvcConfigurer.addInterceptors 程序化配置,類型安全、可測試、易維護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved