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方法總會執行