Spring Mvc中攔截器Interceptor用法解讀
一、概述
攔截器常用于在請(qǐng)求處理的不同階段插入自定義邏輯。
Spring MVC的攔截器作用是在請(qǐng)求到達(dá)控制器之前或之后進(jìn)行攔截,可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行一些特定的處理。
如:
- 登錄驗(yàn)證:對(duì)于需要登錄才能訪問的網(wǎng)址,使用攔截器可以判斷用戶是否已登錄,如果未登錄則跳轉(zhuǎn)到登錄頁面。
- 權(quán)限校驗(yàn):根據(jù)用戶權(quán)限對(duì)部分網(wǎng)址進(jìn)行訪問控制,拒絕未經(jīng)授權(quán)的用戶訪問。
- 請(qǐng)求日志:記錄請(qǐng)求信息,例如請(qǐng)求地址、請(qǐng)求參數(shù)、請(qǐng)求時(shí)間等,用于排查問題和性能優(yōu)化。
- 更改響應(yīng):可以對(duì)響應(yīng)的內(nèi)容進(jìn)行修改,例如添加頭信息、調(diào)整響應(yīng)內(nèi)容格式等。
二、攔截器和過濾器之間的區(qū)別
關(guān)于過濾器可以看我之前的文章過濾器Filter的介紹和使用。
我們很容易發(fā)現(xiàn)攔截器和過濾器十分相似,他們都是對(duì)某一階段的前后進(jìn)行攔截,進(jìn)行一些處理。那么他們之間有什么不同呢?
過濾器(Filter)是servlet中定義的,而攔截器(HandlerInterceptor)則是由Spring MVC框架提供
二者所作用的范圍不同
- 過濾器更注重在**請(qǐng)求和響應(yīng)(即在Servlet之前)**的流程中進(jìn)行處理,可以修改請(qǐng)求和響應(yīng)的內(nèi)容,例如設(shè)置編碼和字符集、請(qǐng)求頭、狀態(tài)碼等。
- 攔截器則更加側(cè)重于對(duì)控制器進(jìn)行前置或后置處理,在請(qǐng)求到達(dá)控制器之前或之后進(jìn)行特定的操作,例如打印日志、權(quán)限驗(yàn)證等。
三、自定義實(shí)現(xiàn)攔截器
Spring MVC 提供了 HandlerInterceptor
接口,開發(fā)者可以通過實(shí)現(xiàn)這個(gè)接口來創(chuàng)建自定義的攔截器。
其中定義了三個(gè)默認(rèn)方法,用于對(duì)不同階段進(jìn)行攔截:
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):
- 在控制器方法執(zhí)行前調(diào)用。
- 返回
true
表示繼續(xù)執(zhí)行后續(xù)的攔截器和控制器方法;返回false
表示中斷執(zhí)行,不再調(diào)用后續(xù)的攔截器和控制器方法。 - 可以用于權(quán)限驗(yàn)證、日志記錄等。
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):
- 在控制器方法執(zhí)行后,但在視圖渲染前調(diào)用。
- 可以用于修改
ModelAndView
對(duì)象,添加額外的數(shù)據(jù)等。
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):
- 在整個(gè)請(qǐng)求處理完成后調(diào)用,無論是否發(fā)生異常。
- 可以用于資源清理、日志記錄等。
創(chuàng)建自定義攔截器:
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在控制器方法執(zhí)行前調(diào)用 System.out.println("preHandle..." ); //這里我們直接返回true return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 在控制器方法執(zhí)行后,但在視圖渲染前調(diào)用 System.out.println("postHandle..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在整個(gè)請(qǐng)求處理完成后調(diào)用 System.out.println("AfterCompletion"); } }
此時(shí)要想要該攔截器生效,我們還需在spring mvc配置文件中進(jìn)行配置(默認(rèn)對(duì)所有控制器進(jìn)行攔截)
<mvc:interceptors> <mvc:interceptor> <!--設(shè)置要攔截的路徑--> <mvc:mapping path="/**"/> <!--指定不進(jìn)行攔截的路徑--> <mvc:exclude-mapping path="/test"/> <!--配置自定義攔截器的類路徑--> <bean class="com.example.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
也可以通過在自定義 攔截器的類上加上@component注解,此時(shí)的配置文件為
<mvc:interceptors> <mvc:interceptor> <!--設(shè)置要攔截的路徑--> <mvc:mapping path="/**"/> <!--指定不進(jìn)行攔截的路徑--> <mvc:exclude-mapping path="/test"/> <!--默認(rèn)名字為類名首字母小寫--> <ref bean="myInterceptor"></ref> </mvc:interceptor> </mvc:interceptors>
四、多個(gè)攔截器的執(zhí)行順序
在 Spring MVC 中,多個(gè)攔截器可以組成一個(gè)攔截器鏈,按照注冊(cè)(配置)順序依次執(zhí)行。
假設(shè)現(xiàn)在按順序注冊(cè)三個(gè)攔截器Interceptor1,Interceptor2,Interceptor3。
當(dāng)所有的攔截器的preHandle方法都返回true時(shí):
- preHandle執(zhí)行順序:Interceptor1->Interceptor2->Interceptor3 (順序執(zhí)行)
- postHandle執(zhí)行順序:Interceptor3->Interceptor2->Interceptor1 (逆序執(zhí)行)
- afterCompletion執(zhí)行順序:Interceptor3->Interceptor2->Interceptor1 (逆序執(zhí)行)
當(dāng)某一個(gè)攔截器的preHandle方法返回false時(shí),這里假設(shè)為Interceptor3
- preHandle執(zhí)行順序:Interceptor1->Interceptor2->Interceptor3 (順序執(zhí)行直到某一個(gè)攔截器返回false)
- postHandle不執(zhí)行,控制器方法也不執(zhí)行
- afterCompletion執(zhí)行順序:Interceptor2->Interceptor1 (返回false的攔截器之前的攔截器逆序執(zhí)行)
為什么是這樣的順序呢?我們觀察源碼可以發(fā)現(xiàn):
- preHandle源碼分析
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { //interceptorList是一個(gè)ArrayList集合,按順序存放了所有的攔截器 //下標(biāo)從0開始,從這里我們可以知道為什么是順序執(zhí)行的。 //this.interceptorIndex = i++,注意這個(gè)代碼,如果返回false,則它的值則表示當(dāng)前返回false的攔截器的下標(biāo) for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); //如果返回false if (!interceptor.preHandle(request, response, this.handler)) { //執(zhí)行AfterCompletion,這里我們就知道為什么不執(zhí)行postHandle,而執(zhí)行AfterCompletion了 this.triggerAfterCompletion(request, response, (Exception)null); return false; } } return true; }
- postHandle源碼分析
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { //可以看到這里是從最后一個(gè)攔截器開始逆序遍歷 for(int i = this.interceptorList.size() - 1; i >= 0; --i) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } }
- afterCompletion源碼分析
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { //this.interceptorList.size() - 1表示當(dāng)前返回false的攔截器的上一個(gè)的下標(biāo) //注意這里是--i //這也就解釋了為什么是返回false的攔截器之前的攔截器逆序執(zhí)行 for(int i = this.interceptorList.size() - 1; i >= 0; --i) { HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i); interceptor.postHandle(request, response, this.handler, mv); } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決分頁插件pagehelper在SpringBoot不起作用的問題
這篇文章主要介紹了解決分頁插件pagehelper在SpringBoot不起作用的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Eclipse 出現(xiàn)Failed to load JavaHL Library解決方法
這篇文章主要介紹了Eclipse 出現(xiàn)Failed to load JavaHL Library解決方法的相關(guān)資料,今天使用Eclipse 時(shí)出現(xiàn)以上錯(cuò)誤,本文說明如何更更正,需要的朋友可以參考下2016-11-11java用list集合存儲(chǔ)學(xué)生信息并算出成績平均值操作
這篇文章主要介紹了java用list集合存儲(chǔ)學(xué)生信息并算出成績平均值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08maven導(dǎo)入本地倉庫jar包,報(bào):Could?not?find?artifact的解決
這篇文章主要介紹了maven導(dǎo)入本地倉庫jar包,報(bào):Could?not?find?artifact的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過程解析
這篇文章主要介紹了Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03