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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringMVC拦截器HandlerInterceptor原理及使用

發(fā)布時間:2025/3/15 javascript 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringMVC拦截器HandlerInterceptor原理及使用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在使用SpringMVC攔截器的時候,我們接觸的最多的便是HandlerInterceptor接口,因為我們所有的自定義攔截器都必須要實現(xiàn)HandlerInterceptor接口,那么就先從HandlerInterceptor接口開始一步步分析。

一、HandlerInterceptor接口

包含三個方法:

default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true; } default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception { }

根據(jù)注釋部分我們知道如下內(nèi)容:

  • preHandle是在找到處理handler對象的HandlerMapping之后,HandlerAdapter調(diào)度handler之前執(zhí)行。

  • postHandle是在HandlerAdapter調(diào)度handler之后,DispatcherServlet渲染視圖之前執(zhí)行,可以通過ModelAndView來向視圖中添加一些信息等。

  • afterCompletion是在渲染視圖結(jié)束后執(zhí)行,主要可以用來進行事后的資源清理。

  • 其中postHandle和afterCompletion方法是反順序執(zhí)行的。也就是說第一個攔截器會最后一個執(zhí)行。關于HandlerInterceptor的執(zhí)行順序我們可以在HandlerExecutionChain類中找到。

  • 二、HandlerExecutionChain類

    這個類由一個handler和若干的HandlerInterceptor構(gòu)成。那么這個類的作用就顯而易見了,就是將攔截器和handle組合起來執(zhí)行。就是對handle進行了包裝。

    這個類中有幾個主要的方法:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {//按順序執(zhí)行for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true; } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {//逆序執(zhí)行for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}} } void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {// 逆序執(zhí)行 for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}} }

    從函數(shù)名中我們可以看出這些方法分別是做什么的,分別是執(zhí)行interceptorList中所有interceptor的preHandle、postHandle和afterCompletion方法。

    先從applyPreHandle()看起,我們發(fā)現(xiàn)這個方法就是做的這樣一個工作,按照列表中interceptor的順序來執(zhí)行它們的preHandle方法,直到有一個返回false。再看一下返回false后這個方法所做的工作,這時會調(diào)用triggerAfterCompletion方法,此時this.interceptorIndex指向上一個返回true的interceptor的位置,所以它會按逆序執(zhí)行所有返回true的interceptor的afterCompletion方法。換言之,也就是對于任意的返回false的interceptor都不會執(zhí)行afterCompletion方法。而且是中斷之前所有的preHandle執(zhí)行完成之后才會執(zhí)行afterCompletion方法。接下來是applyPostHandle(),這個方法較為簡單,就是按照逆序執(zhí)行所有interceptor的postHandle方法。最后的triggerAfterCompletion()也是一樣,就是從最后一次preHandle成功的interceptor處逆序執(zhí)行afterCompletion。

    三、HandlerMapping接口

    HandlerExecutionChain是通過HandlerMapping的getHandler方法返回的。繼承該接口的類是來實現(xiàn)請求和handler對象的映射關系的。

    這個接口中只有這樣一個方法

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

    根據(jù)函數(shù)名,參數(shù)及返回值我們不難猜出這個接口的作用,就是根據(jù)request返回HandlerExecutionChain。至于HandlerMapping在springMVC中有多種實現(xiàn),我們此處就不深究了。

    對于getHandler最后的調(diào)度部分便是springMVC的最外層DispatcherServlet類了

    四、DispatcherServlet類

    DispatcherServlet類中調(diào)用HandlerMapping的getHandler的方法為getHandler(同名)

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}}return null; }

    從代碼中不難看出整個邏輯就是依次判斷servlet中的每個handlerMapping是否能夠匹配該請求,直到找到那個匹配的然后返回處理結(jié)果。

    對于HandlerExecutionChain的調(diào)用我們可以在doDispatch()中找到

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 此處用processedRequest 需要注意的是:若是處理上傳,processedRequest 將和request不再指向同一對象HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;//主要用來管理異步請求的處理。什么時候要用到異步處理呢?就是業(yè)務邏輯復雜(或者其他原因),為了避免請求線程阻塞,需要委托給另一個線程的時候。WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {//檢查是否為上傳文件請求//checkMultipart 這個方法很重要,判斷是否是上傳需求。且看下面的具體分析://如果請求是POST請求,并且請求頭中的Context-Type是以multipart/開頭的就認為是文件上傳的請求processedRequest = checkMultipart(request);//標記一下:是否是文件上傳的requestmultipartRequestParsed = (processedRequest != request);// 步驟1,獲取執(zhí)行鏈,重要// 找到一個處理器,如果沒有找到對應的處理類的話,這里通常會返回404,如果throwExceptionIfNoHandlerFound屬性值為true的情況下會拋出異常mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 步驟2,獲取適配器,重要// 根據(jù)實際的handler去找到一個合適的HandlerAdapter,方法詳細邏輯同getHandler,因此不再解釋HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//如果是GET請求,如果內(nèi)容沒有變化的話,則直接返回String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 步驟3,執(zhí)行攔截器pre方法,重要// 這段代碼很有意思:執(zhí)行處理器連里的攔截器們,具體參閱下面詳細:if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//步驟4,真正處理邏輯,重要// 真正執(zhí)行我們自己書寫的controller方法的邏輯。返回一個ModelAndView// 這也是一個很復雜的過程(序列化、數(shù)據(jù)綁定等等),需要后面專題講解mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 如果異步啟動了,這里就先直接返回了,也就不會再執(zhí)行攔截器PostHandle之類的if (asyncManager.isConcurrentHandlingStarted()) {return;}//意思是:如果我們沒有設置viewName,就采用默認的。否則采用我們自己的applyDefaultViewName(processedRequest, mv);//步驟5,執(zhí)行攔截器post方法,重要// 執(zhí)行所有的攔截器的postHandle方法,并且把mv給他// 這里有一個小細節(jié):這個時候攔截器是【倒序】執(zhí)行的mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}//步驟6,處理視圖,重要//這個方法很重要,顧名思義,他是來處理結(jié)果的,渲染視圖、處理異常等等的 下面詳細分解processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {//步驟7,執(zhí)行攔截器收尾方法,重要triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

    從上面代碼中我們可以驗證第一部分所說的HandlerInterceptor接口中三個方法的執(zhí)行順序:

  • preHandle是在找到處理handler對象的HandlerMapping之后,HandlerAdapter調(diào)度handler之前執(zhí)行。
  • postHandle是在HandlerAdapter調(diào)度handler之后,DispatcherServlet渲染視圖之前執(zhí)行。
  • afterCompletion是在渲染視圖結(jié)束后執(zhí)行。
  • 五、例子

  • 四個攔截器A\B\C\D
  • @Component public class A implements HandlerInterceptor {//在Controller執(zhí)行之前調(diào)用,如果返回false,controller不執(zhí)行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("---------A.preHandle--------");return true;}//controller執(zhí)行之后,且頁面渲染之前調(diào)用@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("---------A.postHandle--------");}//頁面渲染之后調(diào)用,一般用于資源清理操作@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("---------A.afterCompletion--------");} } @Configuration public class MvcConfiguration implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new A());registry.addInterceptor(new B());registry.addInterceptor(new C());registry.addInterceptor(new D());WebMvcConfigurer.super.addInterceptors(registry);} }

    當全部攔截器的preHandle返回值都是true時,調(diào)用攔截器返回如下:

    當C的preHandle返回false時,返回如下:

    總結(jié)

    以上是生活随笔為你收集整理的SpringMVC拦截器HandlerInterceptor原理及使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。