SpringMVC 中的 Interceptor 攔截器是非常重要和相當(dāng)有用的,它的主要作用是攔截指定的用戶請求,并進(jìn)行相應(yīng)的預(yù)處理與后處理。其攔截的時間點(diǎn)在“處理器映射器根據(jù)用戶提交的請求映射出了所要執(zhí)行的處理器類,并且也找到了要執(zhí)行該處理器類的處理器適配器,在處理器適配器執(zhí)行處理器之前”。當(dāng)然,在處理器映射器映射出所要執(zhí)行的處理器類時,已經(jīng)將攔截器與處理器組合為了一個處理器執(zhí)行鏈,并返回給了中央調(diào)度器。
項目:interceptor。
自定義攔截器
自定義攔截器,需要實(shí)現(xiàn) HandlerInterceptor 接口。而該接口中含有三個方法:
● preHandle(request, response, Object handler):
該方法在處理器方法執(zhí)行之前執(zhí)行。其返回值為 boolean,若為 true,則緊接著會執(zhí)行處理器方法,且會將 afterCompletion()方法放入到一個專門的方法棧中等待執(zhí)行。
● postHandle(request, response, Object handler, modelAndView):
該方法在處理器方法執(zhí)行之后執(zhí)行。處理器方法若最終未被執(zhí)行,則該方法不會執(zhí)行。
由于該方法是在處理器方法執(zhí)行完后執(zhí)行,且該方法參數(shù)中包含 ModelAndView,所以該方法可以修改處理器方法的處理結(jié)果數(shù)據(jù),且可以修改跳轉(zhuǎn)方向。
● afterCompletion(request, response, Object handler, Exception ex):
當(dāng) preHandle()方法返回 true 時,會將該方法放到專門的方法棧中,等到對請求進(jìn)行響應(yīng)的所有工作完成之后才執(zhí)行該方法。即該方法是在中央調(diào)度器渲染(數(shù)據(jù)填充)了響應(yīng)頁面之后執(zhí)行的,此時對 ModelAndView 再操作也對響應(yīng)無濟(jì)于事。
攔截器中方法與處理器方法的執(zhí)行順序如下圖:
換一種一表現(xiàn)方式,也可以這樣理解:
1、注冊攔截器
用于指定當(dāng)前所注冊的攔截器可以攔截的請求路徑,而/**表示攔截所 有請求。
2、修改 index 頁面
3、修改處理器
4、修改 show 頁面
5、控制臺輸出結(jié)果
項目:interceptor2。在項目 interceptor 基礎(chǔ)上修改。
1、再定義一個攔截器
2、多個攔截器的注冊與執(zhí)行
3、控制臺執(zhí)行結(jié)果
當(dāng)有多個攔截器時,形成攔截器鏈。攔截器鏈的執(zhí)行順序,與其注冊順序一致。需要再次強(qiáng)調(diào)一點(diǎn)的是,當(dāng)某一個攔截器的 preHandle()方法返回 true 并被執(zhí)行到時,會向一個專門的方法棧中放入該攔截器的 afterCompletion()方法。
多個攔截器中方法與處理器方法的執(zhí)行順序如下圖:
將處理器執(zhí)行鏈返回給中央控制器
從圖中可以看出,只要有一個 preHandle()方法返回 false,則上部的執(zhí)行鏈將被斷開, 其后續(xù)的處理器方法與 postHandle()方法將無法執(zhí)行。但,無論執(zhí)行鏈執(zhí)行情況怎樣,只要方法棧中有方法,即執(zhí)行鏈中只要有 preHandle()方法返回 true,就會執(zhí)行方法棧中的afterCompletion()方法。最終都會給出響應(yīng)。
換一種一表現(xiàn)方式,也可以這樣理解:
4、 閱讀源碼
查看中央調(diào)度器 DispatcherServlet 的 doDispatch()方法源碼:在執(zhí)行處理器方法之前, 會執(zhí)行處理器執(zhí)行鏈對象 mappedHandler 的 applyPreHandle()方法。然后執(zhí)行 Handler,最后 執(zhí)行處理器執(zhí)行鏈對象的 applyPostHandle()方法。
applyPreHandle()方法用于執(zhí)行處理器執(zhí)行鏈中的所有攔截器的 preHandle()方法。
applyPreHandle()方法的返回結(jié)果取決于執(zhí)行鏈中的每一個攔截器的 preHandle()方法。只要 有一個 preHandle()方法返回 false,則其就會返回 false。然后就執(zhí)行了 return;即結(jié)束了 doDispatch()方法,即該請求的處理結(jié)束。
對于處理器執(zhí)行鏈的 applyPostHandle()方法,其是循環(huán)倒序執(zhí)行所有攔截器的 postHandle()方法的。
那么 afterCompletion()方法是什么時候執(zhí)行的呢?
在剛才的處理器執(zhí)行鏈的 applyPreHandle()方法中看到,若存在任一個攔截器的 preHandle()方法返回 false,則會調(diào)用執(zhí)行處理器執(zhí)行鏈的 triggerAfterCompletion()方法,即 會觸發(fā)所有 afterCompletion()方法的執(zhí)行。
在 doDispatch()方法中也存在一個 catch(){}語句,表示若發(fā)生異常,則會調(diào)用執(zhí)行 triggerAfterCompletion()方法。
但在正常情況下,即所有的 preHandle()方法返回均為 true,且 doDispatch()方法沒有異 常發(fā)生的情況下,afterCompletion()方法是在視圖解析器后執(zhí)行的。
查看中央調(diào)度器 DispatcherServlet 的 processDispatchResult()方法源碼可知,在對視圖渲 染過后,會調(diào)用執(zhí)行處理器執(zhí)行鏈的 triggerAfterCompletion() 方法,即執(zhí)行所有的 afterCompletion()方法。
打 開 處 理 器執(zhí) 行 鏈的 triggerAfterCompletion() 方法 , 可 以 看到 , 其對攔 截 器 的 afterCompletion()方法的執(zhí)行,也是循環(huán)倒序執(zhí)行的。
只有經(jīng)過登錄的用戶方可訪問處理器,否則,將返回“無權(quán)訪問”提示。
本例的登錄,由一個 JSP 頁面完成。即在該頁面里將用戶信息放入 session 中。也就是說,只要訪問過該頁面,就說明登錄了。沒訪問過,則為未登錄用戶。
項目:interceptor_permission。在項目 interceptor1 基礎(chǔ)上修改。
1、修改 index 頁面
2、定義 Controller
3、定義 welcome 頁面
4、定義權(quán)限攔截器
當(dāng) preHandle()方法返回 false 時,需要使用 request 或 response 對請求進(jìn)行響應(yīng)。
5、定義 fail 頁面
6、注冊權(quán)限攔截器
7、定義 login 頁面
8、定義 logout 頁面
9、項目測試
Step1:在地址欄先直接提交 system.do 請求
Step2:訪問 login.jsp,進(jìn)行用戶登錄
Step3:再次提交 system.do 請求
Step4:訪問 logout.jsp,進(jìn)行用戶退出
Step5:三次提交 system.do 請求