日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(转)spring中的拦截器(HandlerInterceptor+MethodInterceptor)

發布時間:2023/12/10 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (转)spring中的拦截器(HandlerInterceptor+MethodInterceptor) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.? 過濾器跟攔截器的區別

在說攔截器之前,不得不說一下過濾器,有時候往往被這兩個詞搞的頭大。

其實我們最先接觸的就是過濾器,還記得web.xml中配置的<filter>嗎~

你應該知道spring mvc的攔截器是只攔截controller而不攔截jsp,html 頁面文件的,如果想要攔截那怎么辦?

這就用到過濾器filter了,filter是在servlet前執行的,你也可以理解成過濾器中包含攔截器,一個請求過來 ,先進行過濾器處理,看程序是否受理該請求 。 過濾器放過后 , 程序中的攔截器進行處理 。

(1)過濾器(Filter):當你有一堆東西的時候,你只希望選擇符合你要求的某一些東西。定義這些要求的工具,就是過濾器。(理解:就是一堆字母中取一個B)

(2)攔截器(Interceptor):在一個流程正在進行的時候,你希望干預它的進展,甚至終止它進行,這是攔截器做的事情。(理解:就是一堆字母中,干預他,通過驗證的少點,順便干點別的東西)。

2.? spring中的攔截器

在web開發中,攔截器是經常用到的功能。它可以幫我們驗證是否登陸、預先設置數據以及統計方法的執行效率等等。

今天就來詳細的談一下spring中的攔截器。spring中攔截器主要分兩種,一個是HandlerInterceptor,一個是MethodInterceptor。

2.1??HandlerInterceptor攔截器

HandlerInterceptor是springMVC項目中的攔截器,它攔截的目標是請求的地址,比MethodInterceptor先執行。

實現一個HandlerInterceptor攔截器可以直接實現HandlerInterceptor接口,也可以繼承HandlerInterceptorAdapter類。

這兩種方法殊途同歸,其實HandlerInterceptorAdapter也就是聲明了HandlerInterceptor接口中所有方法的默認實現,而我們在繼承他之后只需要重寫必要的方法。

下面就是HandlerInterceptorAdapter的代碼,可以看到一個方法只是默認返回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 { } }

這三個方法都是干什么的,有什么作用,什么時候調用,不同的攔截器之間是怎樣的調用順序呢?

先補一張圖:

這還得參考一下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處理請求的整個過程。首先根據請求找到對應的HandlerExecutionChain,它包含了處理請求的handler和所有的HandlerInterceptor攔截器;然后在調用hander之前分別調用每個HandlerInterceptor攔截器的preHandle方法,若有一個攔截器返回false,則會調用triggerAfterCompletion方法,并且立即返回不再往下執行;若所有的攔截器全部返回true并且沒有出現異常,則調用handler返回ModelAndView對象;再然后分別調用每個攔截器的postHandle方法;最后,即使是之前的步驟拋出了異常,也會執行triggerAfterCompletion方法。關于攔截器的處理到此為止,接下來看看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做的事情就是從當前的攔截器開始逆向調用每個攔截器的afterCompletion方法,并且捕獲它的異常,也就是說每個攔截器的afterCompletion方法都會調用。

根據以上的代碼,分析一下不同攔截器及其方法的執行順序。假設有5個攔截器編號分別為12345,若一切正常則方法的執行順序是12345的preHandle,54321的postHandle,54321的afterCompletion。若編號3的攔截器的preHandle方法返回false或者拋出了異常,接下來會執行的是21的afterCompletion方法。這里要注意的地方是,我們在寫一個攔截器的時候要謹慎的處理preHandle中的異常,因為這里一旦有異常拋出就不會再受到這個攔截器的控制。12345的preHandle的方法執行過之后,若handler出現了異常或者某個攔截器的postHandle方法出現了異常,則接下來都會執行54321的afterCompletion方法,因為只要12345的preHandle方法執行完,當前攔截器的攔截器就會記錄成編號5的攔截器,而afterCompletion總是從當前的攔截器逆向的向前執行。

2.2??MethodInterceptor攔截器

MethodInterceptor是AOP項目中的攔截器,它攔截的目標是方法,即使不是controller中的方法。實現MethodInterceptor攔截器大致也分為兩種,一種是實現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,當然你也可以在配置文件中聲明這個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; } }

當然,這一切都離不開配置,具體看配置中的注釋

<!-- 自定義攔截器 ,先過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..*.*(..))" /> <!--在該切入點使用自定義攔截器 ,按照先后順序執行 --> <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就能正常工作了。其實,這兩種實現方最終...沒錯,還是殊途同歸。

aspectj的攔截器會被解析成AOP中的advice,最終被適配成MethodInterceptor,詳細的過程請參考springAOP的實現。

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>

經測試發現,interceptor先于AOP執行。

4.? 談一談區別

上面的兩種攔截器都能起到攔截的效果,但是他們攔截的目標不一樣,實現的機制不同,所以有的時候適用不同的場景。

HandlerInterceptoer攔截的是請求地址,所以針對請求地址做一些驗證、預處理等操作比較合適。當你需要統計請求的響應時間時MethodInterceptor將不太容易做到,因為它可能跨越很多方法或者只涉及到已經定義好的方法中一部分代碼。MethodInterceptor利用的是AOP的實現機制,在本文中只說明了使用方式,關于原理和機制方面介紹的比較少,因為要說清楚這些需要講出AOP的相當一部分內容。在對一些普通的方法上的攔截HandlerInterceptoer就無能為力了,這時候只能利用AOP的MethodInterceptor。


另外,還有一個跟攔截器類似的東西----Filter。Filter是Servlet規范規定的,不屬于spring框架,也是用于請求的攔截。但是它適合更粗粒度的攔截,在請求前后做一些編解碼處理、日志記錄等。而攔截器則可以提供更細粒度的,更加靈活的,針對某些請求、某些方法的組合的解決方案。


另外的另外,用過人人網的ROSE框架的人都會非常喜歡它的攔截器功能。因為它實現了全注解的方式,只要在類的名字上加上攔截器的注解即表示這是一個攔截器。而使用這個攔截器的方法或者controller也只需在方法或controller的上面加上這個攔截器的注解。其實這是一個關注點的轉變,spring的切面控制在配置文件中,配置文件關注哪些地方需要攔截。而在ROSE中,則是在需要攔截的地方關注我要被誰攔截。

總結

以上是生活随笔為你收集整理的(转)spring中的拦截器(HandlerInterceptor+MethodInterceptor)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。