1. 認(rèn)識攔截器
Spring MVC的攔截器(Interceptor)不是Filter,同樣可以實現(xiàn)請求的預(yù)處理、后處理。使用攔截器僅需要兩個步驟:
- 實現(xiàn)攔截器
- 注冊攔截器
1.1 實現(xiàn)攔截器
實現(xiàn)攔截器可以自定義實現(xiàn)HandlerInterceptor接口,也可以通過繼承HandlerInterceptorAdapter類,后者是前者的實現(xiàn)類。下面是攔截器的一個實現(xiàn)的例子,目的是判斷用戶是否登錄。如果preHandle方法return true,則繼續(xù)后續(xù)處理。
public class LoginInterceptor extends HandlerInterceptorAdapter { /** *預(yù)處理回調(diào)方法,實現(xiàn)處理器的預(yù)處理(如登錄檢查)。 *第三個參數(shù)為響應(yīng)的處理器,即controller。 *返回true,表示繼續(xù)流程,調(diào)用下一個攔截器或者處理器。 *返回false,表示流程中斷,通過response產(chǎn)生響應(yīng)。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("-------------------preHandle"); // 驗證用戶是否登陸 Object obj = request.getSession().getAttribute("username"); if (obj == null || !(obj instanceof String)) { response.sendRedirect(request.getContextPath() + "/index.html"); return false; } return true; } /** *當(dāng)前請求進(jìn)行處理之后,也就是Controller 方法調(diào)用之后執(zhí)行, *但是它會在DispatcherServlet 進(jìn)行視圖返回渲染之前被調(diào)用。 *此時我們可以通過modelAndView對模型數(shù)據(jù)進(jìn)行處理或?qū)σ晥D進(jìn)行處理。 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("-------------------postHandle"); } /** *方法將在整個請求結(jié)束之后,也就是在DispatcherServlet 渲染了對應(yīng)的視圖之后執(zhí)行。 *這個方法的主要作用是用于進(jìn)行資源清理工作的。 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("-------------------afterCompletion"); } }
1.2 注冊攔截器
為了使自定義的攔截器生效,需要注冊攔截器到spring容器中,具體的做法是繼承WebMvcConfigurerAdapter類,覆蓋其addInterceptors(InterceptorRegistry registry)方法。最后別忘了把Bean注冊到Spring容器中,可以選擇@Component 或者 @Configuration。
@Component public class InterceptorConfiguration extends WebMvcConfigurerAdapter{ @Override public void addInterceptors(InterceptorRegistry registry) { // 注冊攔截器 InterceptorRegistration ir = registry.addInterceptor(new LoginInterceptor()); // 配置攔截的路徑 ir.addPathPatterns("/**"); // 配置不攔截的路徑 ir.excludePathPatterns("/**.html"); // 還可以在這里注冊其它的攔截器 //registry.addInterceptor(new OtherInterceptor()).addPathPatterns("/**"); } }
1.3 攔截器的應(yīng)用場景
攔截器本質(zhì)上是面向切面編程(AOP),符合橫切關(guān)注點的功能都可以放在攔截器中來實現(xiàn),主要的應(yīng)用場景包括:
- 登錄驗證,判斷用戶是否登錄。
- 權(quán)限驗證,判斷用戶是否有權(quán)限訪問資源。
- 日志記錄,記錄請求日志,以便統(tǒng)計請求訪問量。
- 處理cookie、本地化、國際化、主題等。
- 性能監(jiān)控,監(jiān)控請求處理時長等。
2. 原理
2.1 工作原理
攔截器不是Filter,卻實現(xiàn)了Filter的功能,其原理在于:
- 所有的攔截器(Interceptor)和處理器(Handler)都注冊在HandlerMapping中。
- Spring MVC中所有的請求都是由DispatcherServlet分發(fā)的。
- 當(dāng)請求進(jìn)入DispatcherServlet.doDispatch()時候,首先會得到處理該請求的Handler(即Controller中對應(yīng)的方法)以及所有攔截該請求的攔截器。攔截器就是在這里被調(diào)用開始工作的。
2.2 攔截器工作流程
一個攔截器,只有preHandle方法返回true,postHandle、afterCompletion才有可能被執(zhí)行;如果preHandle方法返回false,則該攔截器的postHandle、afterCompletion必然不會被執(zhí)行。
假設(shè)我們有兩個攔截器,例如叫Interceptor1和Interceptor2,當(dāng)一個請求過來,正常的流程和中斷的流程分別如下。
2.2.1正常流程
注意兩個攔截器在執(zhí)行preHandle方法和執(zhí)行postHandle、afterCompletion方法時,順序是顛倒的。
- Interceptor1.preHandle
- Interceptor2.preHandle
- Controller處理請求
- Interceptor2.postHandle
- Interceptor1.postHandle
- 渲染視圖
- Interceptor2.afterCompletion
- Interceptor1.afterCompletion
2.2.2 中斷流程
假設(shè)執(zhí)行Interceptor2.preHandle中報錯,那么流程被中斷,之前被執(zhí)行過的攔截器的afterCompletion仍然會執(zhí)行。在本例中,即執(zhí)行了Interceptor1.afterCompletion。
1. Interceptor1.preHandle 2. Interceptor2.preHandle //中間流程被中斷,不再執(zhí)行 3. Interceptor1.afterCompletion
2.3 和Filter共存時的執(zhí)行順序
攔截器是在DispatcherServlet這個servlet中執(zhí)行的,因此所有的請求最先進(jìn)入Filter,最后離開Filter。其順序如下。
Filter->Interceptor.preHandle->Handler->Interceptor.postHandle->Interceptor.afterCompletion->Filter
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。