Spring MVC 攔截器配置
1 簡介
攔截器,在程序進入核心邏輯之前和之後附加部分額外處理邏輯,實用場景舉例如下:
- 鑑權:在程序進入核心處理之前,判定是否爲非法請求;
- 統計:對請求次數做數量統計;
- 監控:在程序處理之前和之後對時間打點,計算核心邏輯消耗時間。```
與Struts類似,Spring MVC 可配置攔截器棧,先配先執行。Spring MVC可理解爲入棧一次(preHandler),出棧兩次(postHandler 和 afterCompletion)。
2 接口說明
Spring MVC攔截器接口爲HandlerInterceptor,通過實現該接口方法即可完成自定義攔截器。
public interface HandlerInterceptor {
boolean preHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)
throws Exception;
}
可發現,HandlerInterceptor共有三個接口,分別爲preHandler,postHandler,afterCompletion,其功能介紹如下:
- preHandler:進入核心邏輯(即Controller)之前攔截器,返回值:true表示通過當前攔截邏輯,進入下一個攔截器的preHandler,當所有攔截器通過之後進入核心邏輯Controller;false表示未通過當前攔截邏輯,流程中斷,然後逆序執行所有返回true的攔截器的afterCompletion;
- postHandler:Controller執行之後,按照preHandler的處理順序,逆序執行所有攔截器的postHandler,然後進行視圖渲染,此爲第一次出棧;
- afterCompletion:在視圖渲染之後,再按照postHandler的處理順序執行所有afterCompletion,該部分可進行的工作包括資源清理、統計時間等,此爲第二次出棧。
由於實現HandlerInterceptor每次都需要實現所有接口,爲了簡化開發,Spring非常貼心的爲我們提供了一個適配器HandlerInterceptorAdapter,允許我們只實現需要的回調方法。
3 代碼實現
3.1 攔截器配置
攔截器的攔截依據是:請求url,請注意以下url的 區別:
- / :攔截url爲/的請求
- /* :攔截父url爲/所有請求
- /** :攔截祖先url爲/所有請求
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--mvc需要的Controller必須在此聲明-->
<context:component-scan base-package="spring.mvc"/>
<!-- 啓用spring mvc 註解 -->
<mvc:annotation-driven/>
<!--攔截器配置,多個攔截器,順序執行-->
<mvc:interceptors>
<mvc:interceptor>
<!--攔截url爲/的請求-->
<mvc:mapping path="/"/>
<!--攔截父url爲/所有請求-->
<mvc:mapping path="/*"/>
<!--攔截祖先url爲/的所有請求-->
<mvc:mapping path="/**"/>
<!--不攔截指定url的請求-->
<mvc:exclude-mapping path="/mvc/setPerson"/>
<!--自定義攔截器-->
<bean id="interceptor" class="spring.interceptor.BaseInterceptor"/>
</mvc:interceptor>
<!-- 當設置多個攔截器時,先按順序調用preHandle方法,然後逆序調用每個攔截器的postHandle和afterCompletion方法 -->
</mvc:interceptors>
</beans>
3.2 自定義攔截器實現
public class BaseInterceptor implements HandlerInterceptor {
/**
* 在進入核心邏輯之前執行
* 返回值:false
* 從當前攔截器逆向執行所有攔截器的afterCompletion()
* 返回值:true
* 執行下一個攔截器,知道所有攔截器的preHandle()都執行完成(可以理解爲入棧過程)
* 然後執行核心邏輯Controller
* 接着對所有攔截器出棧,執行postHandle()
* 最後在做一次出棧,執行所有的afterCompletion()
*/
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("==========BaseInterceptor.preHandle============");
return true;
}
/**
* 在覈心邏輯之後,視圖渲染之前執行
* 可向modelAndView添加數據,如時間,cookie等
*/
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("==========BaseInterceptor.postHandle============");
}
/**
* 在整個請求完成之後執行,可用於清理資源
* 如果有攔截器拋異常,則從當前攔截器逆向執行所有afterCompletion()
*/
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("==========BaseInterceptor.afterCompletion============");
}
}
3.3.3 測試結果
postman輸入鏈接:http://localhost:8070/mvc/setInfo/zhangsan?sex=male&addr=上海
控制檯輸出結果:
====BaseInterceptor.preHandle====
收到用戶名稱:zhangsan
收到用戶性別:male
收到用戶地址:上海
====BaseInterceptor.postHandle====
====BaseInterceptor.afterCompletion====
3.3.4 總結
從上述測試結果來看,成功配置了攔截器,器攔截器的執行順序是:preHandler -> Controller -> postHandler -> afterCompletion