javascript
SpringMVC拦截器HandlerInterceptor原理及使用
在使用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返回值都是true時,調(diào)用攔截器返回如下:
當C的preHandle返回false時,返回如下:
總結(jié)
以上是生活随笔為你收集整理的SpringMVC拦截器HandlerInterceptor原理及使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SaaS的行业概述及发展现状
- 下一篇: 2020年短视频用户价值研究报告