Spring 全家桶之 Spring Web MVC(七)- Interceptor

語言: CN / TW / HK

highlight: a11y-light

一起養成寫作習慣!這是我參與「掘金日新計劃 · 4 月更文挑戰」的第11天,點選檢視活動詳情

一、攔截器

Spring MVC提供了攔截器機制,允許在執行目標方法前進行一些攔截工作,或者在目標方法執行之後進行一些其他處理

Spring MVC 中的攔截器是HandlerInterceptor介面,該介面包含了三個方法

image.png - preHandler:這個方法在業務處理器處理請求之前被呼叫,在該方法中對使用者請求進行處理,如果需要該攔截器對請求進行攔截處理後還要呼叫其他攔截器,或者是業務處理器進行處理,則返回True既放行請求,如果不需要再呼叫其他元件就返回false,既不放行請求 - postHandler:這個方法在業務處理器處理完成請求後呼叫,但是DispatcherServlet向客戶端返回響應前被呼叫,在該方法中對使用者請求request進行處理 - afterCompletion:這個方法在DispatcherServlet完全處理請求後被呼叫,可以在該方法中進行一些資源清理的操作

二、自定義攔截器

拷貝spring-mvc-ajax專案,並重命名為spring-mvc-handler,刪除除了配置之外的類及檔案。

攔截器的正常流程

新建一個HandlerInterceptorSamplerController,在該Controller中定義interceptor方法,測試自定義的攔截器,並返回success頁面 ```java @Controller public class HandlerInterceptorSamplerController {

@RequestMapping("/interceptor")
public String interceptor(){

    System.out.println("interceptor方法被呼叫");
    return "success";
}

} 在index.jsp頁面增加一個超連結jsp 攔截該請求 新增interceptor包,新建一個自定義的攔截器ZuluInterceptor,自定義攔截器必須實現HandlerInterceptor介面,在攔截器中的每個方法中添加了日誌列印java public class ZuluInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    System.out.println(this.getClass().getName() + " preHandler方法運行了");
    return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println(this.getClass().getName() + " postHandle方法運行了");
    System.out.println(modelAndView.getViewName());
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println(this.getClass().getName() + " afterCompletion方法運行了");
}

} 在Spring MVC 配置檔案中註冊攔截器,配置這個攔截器攔截哪些請求xml ```

啟動應用,點選首頁的超連結

image.png 根據控制檯的輸出,自定義的攔截器被成功呼叫

因此攔截器的正常流程是:攔截器preHandler方法 -> 目標方法 -> 攔截器的postHandler方法 -> 頁面渲染 -> 攔截器的afterCompletion方法

攔截器的異常流程

異常流程1 - preHandler返回false

在ZuluInterceptor攔截器的preHandler方法返回false,再次啟動,點選首頁的超連結 image.png 控制檯只輸出了preHandler方法的執行資訊,因此只要preHandler返回false,既不放行就不會有以後的方法的執行。

異常流程2 - 其他異常

保持preHandler方法返回true,同時在Controller中的interceptor方法返回頁面前增加異常程式碼 ```java @RequestMapping("/interceptor") public String interceptor(){

System.out.println("interceptor方法被呼叫");
// 異常程式碼
int i = 10 / 0;
return "success";

} ``` 再次重新啟動應用,點選頁面的超連結 image.png 頁面出現有異常程式碼導致的報錯

image.png 此時控制檯執行了afterCompletion方法

三、多個攔截器執行順序

在interceptor包中拷貝ZuluInterceptor並重命名為DeltaInterceptor;在Spring MVC配置檔案中註冊新定義的攔截器 xml <mvc:interceptors> <!--第一種方式配置某個攔截器,預設是攔截所有請求的--> <bean class="com.citi.interceptor.ZuluInterceptor"></bean> <bean class="com.citi.interceptor.DeltaInterceptor"></bean> </mvc:interceptors>

將Controller中的interceptor方法中的異常程式碼登出,重新啟動,點選頁面的插連線

image.png 根據控制檯的輸出可以確定,限制性了Zulu攔截器中的preHandler方法,接著執行Delta攔截器的preHandler方法,再執行目標方法,接著呼叫Delta攔截器的postHandler,再執行Zulu攔截器的postHandler,再執行Delta攔截器的afterCompletion方法,最後再執行Zulu攔截器的afterCompletion方法

攔截順序: 攔截器攔截順序是按照配置的先後順序,調整攔截器配置順序 xml <mvc:interceptors> <!--第一種方式配置某個攔截器,預設是攔截所有請求的--> <bean class="com.citi.interceptor.DeltaInterceptor"></bean> <bean class="com.citi.interceptor.ZuluInterceptor"></bean> </mvc:interceptors>

再次啟動,點選首頁的超連結 image.png 根據控制檯輸出,配置檔案中先配置的Delta攔截器最先執行了

多個攔截器的異常流程:

保持Spring MVC配置檔案中Delta攔截器在前,Zulu攔截器在後的順序。如果Delta攔截器不放行,也就沒有後面所有的呼叫;如果Zulu攔截器不放行,會是什麼結果?

在Zulu攔截器中返回false,重新啟動應用,並點選首頁的超連結

image.png 根據控制檯的輸出可以確定,即是Zulu攔截器不放行,但是Delta的afterCompletion方法還是會執行。

已放行了的攔截器的afterCompletion方法總會執行