(转)spring中的拦截器(HandlerInterceptor+MethodInterceptor)
1.? 過濾器跟攔截器的區(qū)別
在說攔截器之前,不得不說一下過濾器,有時候往往被這兩個詞搞的頭大。
其實我們最先接觸的就是過濾器,還記得web.xml中配置的<filter>嗎~
你應(yīng)該知道spring mvc的攔截器是只攔截controller而不攔截jsp,html 頁面文件的,如果想要攔截那怎么辦?
這就用到過濾器filter了,filter是在servlet前執(zhí)行的,你也可以理解成過濾器中包含攔截器,一個請求過來 ,先進(jìn)行過濾器處理,看程序是否受理該請求 。 過濾器放過后 , 程序中的攔截器進(jìn)行處理 。
(1)過濾器(Filter):當(dāng)你有一堆東西的時候,你只希望選擇符合你要求的某一些東西。定義這些要求的工具,就是過濾器。(理解:就是一堆字母中取一個B)
(2)攔截器(Interceptor):在一個流程正在進(jìn)行的時候,你希望干預(yù)它的進(jìn)展,甚至終止它進(jìn)行,這是攔截器做的事情。(理解:就是一堆字母中,干預(yù)他,通過驗證的少點,順便干點別的東西)。
2.? spring中的攔截器
在web開發(fā)中,攔截器是經(jīng)常用到的功能。它可以幫我們驗證是否登陸、預(yù)先設(shè)置數(shù)據(jù)以及統(tǒng)計方法的執(zhí)行效率等等。
今天就來詳細(xì)的談一下spring中的攔截器。spring中攔截器主要分兩種,一個是HandlerInterceptor,一個是MethodInterceptor。
2.1??HandlerInterceptor攔截器
HandlerInterceptor是springMVC項目中的攔截器,它攔截的目標(biāo)是請求的地址,比MethodInterceptor先執(zhí)行。
實現(xiàn)一個HandlerInterceptor攔截器可以直接實現(xiàn)HandlerInterceptor接口,也可以繼承HandlerInterceptorAdapter類。
這兩種方法殊途同歸,其實HandlerInterceptorAdapter也就是聲明了HandlerInterceptor接口中所有方法的默認(rèn)實現(xiàn),而我們在繼承他之后只需要重寫必要的方法。
下面就是HandlerInterceptorAdapter的代碼,可以看到一個方法只是默認(rèn)返回true,另外兩個是空方法:
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor { /** * This implementation always returns <code>true</code>. */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } /** * This implementation is empty. */ public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /** * This implementation is empty. */ public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }這三個方法都是干什么的,有什么作用,什么時候調(diào)用,不同的攔截器之間是怎樣的調(diào)用順序呢?
先補(bǔ)一張圖:
這還得參考一下DispatcherServlet的doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; try { ModelAndView mv; boolean errorView = false; try { processedRequest = checkMultipart(request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // Apply preHandle methods of registered interceptors. HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // Do we need view name translation? if (mv != null && !mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } // Apply postHandle methods of registered interceptors. if (interceptors != null) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } } catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); } catch (Exception ex) { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv != null); } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } // Trigger after-completion for successful outcome. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); } catch (Exception ex) { // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } catch (Error err) { ServletException ex = new NestedServletException("Handler processing failed", err); // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } finally { // Clean up any resources used by a multipart request. if (processedRequest != request) { cleanupMultipart(processedRequest); } } } View Code代碼有點長,但是它封裝了springMVC處理請求的整個過程。首先根據(jù)請求找到對應(yīng)的HandlerExecutionChain,它包含了處理請求的handler和所有的HandlerInterceptor攔截器;然后在調(diào)用hander之前分別調(diào)用每個HandlerInterceptor攔截器的preHandle方法,若有一個攔截器返回false,則會調(diào)用triggerAfterCompletion方法,并且立即返回不再往下執(zhí)行;若所有的攔截器全部返回true并且沒有出現(xiàn)異常,則調(diào)用handler返回ModelAndView對象;再然后分別調(diào)用每個攔截器的postHandle方法;最后,即使是之前的步驟拋出了異常,也會執(zhí)行triggerAfterCompletion方法。關(guān)于攔截器的處理到此為止,接下來看看triggerAfterCompletion做了什么:
private void triggerAfterCompletion(HandlerExecutionChain mappedHandler, int interceptorIndex, HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { // Apply afterCompletion methods of registered interceptors. if (mappedHandler != null) { HandlerInterceptor[] interceptors = mappedHandler.getInterceptors(); if (interceptors != null) { for (int i = interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, mappedHandler.getHandler(), ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } } } View Code?triggerAfterCompletion做的事情就是從當(dāng)前的攔截器開始逆向調(diào)用每個攔截器的afterCompletion方法,并且捕獲它的異常,也就是說每個攔截器的afterCompletion方法都會調(diào)用。
根據(jù)以上的代碼,分析一下不同攔截器及其方法的執(zhí)行順序。假設(shè)有5個攔截器編號分別為12345,若一切正常則方法的執(zhí)行順序是12345的preHandle,54321的postHandle,54321的afterCompletion。若編號3的攔截器的preHandle方法返回false或者拋出了異常,接下來會執(zhí)行的是21的afterCompletion方法。這里要注意的地方是,我們在寫一個攔截器的時候要謹(jǐn)慎的處理preHandle中的異常,因為這里一旦有異常拋出就不會再受到這個攔截器的控制。12345的preHandle的方法執(zhí)行過之后,若handler出現(xiàn)了異常或者某個攔截器的postHandle方法出現(xiàn)了異常,則接下來都會執(zhí)行54321的afterCompletion方法,因為只要12345的preHandle方法執(zhí)行完,當(dāng)前攔截器的攔截器就會記錄成編號5的攔截器,而afterCompletion總是從當(dāng)前的攔截器逆向的向前執(zhí)行。
2.2??MethodInterceptor攔截器
MethodInterceptor是AOP項目中的攔截器,它攔截的目標(biāo)是方法,即使不是controller中的方法。實現(xiàn)MethodInterceptor攔截器大致也分為兩種,一種是實現(xiàn)MethodInterceptor接口,另一種利用AspectJ的注解或配置。 下面是第一種方法的示例 public class MethodInvokeInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("before method invoke"); Object object = methodInvocation.proceed(); System.out.println("after method invoke"); return object; } }下面是基于注解的AspectJ方式
@Component @Aspect public class AutoAspectJInterceptor { @Around("execution (* com.test.controller..*.*(..))") public Object around(ProceedingJoinPoint point) throws Throwable{ System.out.println("AutoAspectJInterceptor begin around"); Object object = point.proceed(); System.out.println("AutoAspectJInterceptor end around"); return object; } }下面是一個用于支持AspectJ方式攔截的普通的bean,當(dāng)然你也可以在配置文件中聲明這個bean
@Component public class AspectJInterceptor { public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("AspectJInterceptor around before"); Object object = proceedingJoinPoint.proceed(); System.out.println("AspectJInterceptor around after"); return object; } }當(dāng)然,這一切都離不開配置,具體看配置中的注釋
<!-- 自定義攔截器 ,先過mvc:interceptors--> <bean id="methodInvokeInterceptor" class="com.test.interceptor.MethodInvokeInterceptor"/> <bean id="aspectInterceptor" class="com.test.interceptor.AspectJInterceptor"/> <aop:config> <!--切入點,controlller --> <aop:pointcut id="pointcut_test" expression="execution(* com.test.controller..*.*(..))" /> <!--在該切入點使用自定義攔截器 ,按照先后順序執(zhí)行 --> <aop:advisor pointcut-ref="pointcut_test" advice-ref="methodInvokeInterceptor" /> <aop:aspect ref="aspectInterceptor"> <aop:around method="around" pointcut="execution(* com.test.controller..*.*(..))"/> </aop:aspect> </aop:config> <!-- 自動掃描使用了aspectj注解的類 --> <aop:aspectj-autoproxy/>通過上面的配置三個MethodInterceptor就能正常工作了。其實,這兩種實現(xiàn)方最終...沒錯,還是殊途同歸。
aspectj的攔截器會被解析成AOP中的advice,最終被適配成MethodInterceptor,詳細(xì)的過程請參考springAOP的實現(xiàn)。
3.? 實例選擇攔截器
項目中采用Interceptor來過濾URL來決定哪些可以在不登錄的情況下訪問,哪些必須要登錄才可以訪問;
3.1? HandlerInterceptor方式
public class SessionTimeoutInterceptor implements HandlerInterceptor {......... }此時需要在servlet.xml中配置<mvc:interceptor>
?
3.2? MethodInterceptor注解Aspect方式
@Component @Aspect public void class BindingResultAop{........ }同時在servlet.xml中配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
經(jīng)測試發(fā)現(xiàn),interceptor先于AOP執(zhí)行。
4.? 談一談區(qū)別
上面的兩種攔截器都能起到攔截的效果,但是他們攔截的目標(biāo)不一樣,實現(xiàn)的機(jī)制不同,所以有的時候適用不同的場景。
HandlerInterceptoer攔截的是請求地址,所以針對請求地址做一些驗證、預(yù)處理等操作比較合適。當(dāng)你需要統(tǒng)計請求的響應(yīng)時間時MethodInterceptor將不太容易做到,因為它可能跨越很多方法或者只涉及到已經(jīng)定義好的方法中一部分代碼。MethodInterceptor利用的是AOP的實現(xiàn)機(jī)制,在本文中只說明了使用方式,關(guān)于原理和機(jī)制方面介紹的比較少,因為要說清楚這些需要講出AOP的相當(dāng)一部分內(nèi)容。在對一些普通的方法上的攔截HandlerInterceptoer就無能為力了,這時候只能利用AOP的MethodInterceptor。
 另外,還有一個跟攔截器類似的東西----Filter。Filter是Servlet規(guī)范規(guī)定的,不屬于spring框架,也是用于請求的攔截。但是它適合更粗粒度的攔截,在請求前后做一些編解碼處理、日志記錄等。而攔截器則可以提供更細(xì)粒度的,更加靈活的,針對某些請求、某些方法的組合的解決方案。
 另外的另外,用過人人網(wǎng)的ROSE框架的人都會非常喜歡它的攔截器功能。因為它實現(xiàn)了全注解的方式,只要在類的名字上加上攔截器的注解即表示這是一個攔截器。而使用這個攔截器的方法或者controller也只需在方法或controller的上面加上這個攔截器的注解。其實這是一個關(guān)注點的轉(zhuǎn)變,spring的切面控制在配置文件中,配置文件關(guān)注哪些地方需要攔截。而在ROSE中,則是在需要攔截的地方關(guān)注我要被誰攔截。
總結(jié)
以上是生活随笔為你收集整理的(转)spring中的拦截器(HandlerInterceptor+MethodInterceptor)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 整理最全的Java笔试题库之问答题篇-国
- 下一篇: 各代iphone尺寸_iPhone尺寸
