javascript
SpringMVC拦截器HandlerInterceptor原理及使用
在使用SpringMVC攔截器的時候,我們接觸的最多的便是HandlerInterceptor接口,因為我們所有的自定義攔截器都必須要實現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 { }根據注釋部分我們知道如下內容:
preHandle是在找到處理handler對象的HandlerMapping之后,HandlerAdapter調度handler之前執行。
postHandle是在HandlerAdapter調度handler之后,DispatcherServlet渲染視圖之前執行,可以通過ModelAndView來向視圖中添加一些信息等。
afterCompletion是在渲染視圖結束后執行,主要可以用來進行事后的資源清理。
其中postHandle和afterCompletion方法是反順序執行的。也就是說第一個攔截器會最后一個執行。關于HandlerInterceptor的執行順序我們可以在HandlerExecutionChain類中找到。
二、HandlerExecutionChain類
這個類由一個handler和若干的HandlerInterceptor構成。那么這個類的作用就顯而易見了,就是將攔截器和handle組合起來執行。就是對handle進行了包裝。
這個類中有幾個主要的方法:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {//按順序執行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)) {//逆序執行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)) {// 逆序執行 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);}}} }從函數名中我們可以看出這些方法分別是做什么的,分別是執行interceptorList中所有interceptor的preHandle、postHandle和afterCompletion方法。
先從applyPreHandle()看起,我們發現這個方法就是做的這樣一個工作,按照列表中interceptor的順序來執行它們的preHandle方法,直到有一個返回false。再看一下返回false后這個方法所做的工作,這時會調用triggerAfterCompletion方法,此時this.interceptorIndex指向上一個返回true的interceptor的位置,所以它會按逆序執行所有返回true的interceptor的afterCompletion方法。換言之,也就是對于任意的返回false的interceptor都不會執行afterCompletion方法。而且是中斷之前所有的preHandle執行完成之后才會執行afterCompletion方法。接下來是applyPostHandle(),這個方法較為簡單,就是按照逆序執行所有interceptor的postHandle方法。最后的triggerAfterCompletion()也是一樣,就是從最后一次preHandle成功的interceptor處逆序執行afterCompletion。
三、HandlerMapping接口
HandlerExecutionChain是通過HandlerMapping的getHandler方法返回的。繼承該接口的類是來實現請求和handler對象的映射關系的。
這個接口中只有這樣一個方法
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;根據函數名,參數及返回值我們不難猜出這個接口的作用,就是根據request返回HandlerExecutionChain。至于HandlerMapping在springMVC中有多種實現,我們此處就不深究了。
對于getHandler最后的調度部分便是springMVC的最外層DispatcherServlet類了
四、DispatcherServlet類
DispatcherServlet類中調用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是否能夠匹配該請求,直到找到那個匹配的然后返回處理結果。
對于HandlerExecutionChain的調用我們可以在doDispatch()中找到
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 此處用processedRequest 需要注意的是:若是處理上傳,processedRequest 將和request不再指向同一對象HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;//主要用來管理異步請求的處理。什么時候要用到異步處理呢?就是業務邏輯復雜(或者其他原因),為了避免請求線程阻塞,需要委托給另一個線程的時候。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,獲取執行鏈,重要// 找到一個處理器,如果沒有找到對應的處理類的話,這里通常會返回404,如果throwExceptionIfNoHandlerFound屬性值為true的情況下會拋出異常mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 步驟2,獲取適配器,重要// 根據實際的handler去找到一個合適的HandlerAdapter,方法詳細邏輯同getHandler,因此不再解釋HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//如果是GET請求,如果內容沒有變化的話,則直接返回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,執行攔截器pre方法,重要// 這段代碼很有意思:執行處理器連里的攔截器們,具體參閱下面詳細:if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//步驟4,真正處理邏輯,重要// 真正執行我們自己書寫的controller方法的邏輯。返回一個ModelAndView// 這也是一個很復雜的過程(序列化、數據綁定等等),需要后面專題講解mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 如果異步啟動了,這里就先直接返回了,也就不會再執行攔截器PostHandle之類的if (asyncManager.isConcurrentHandlingStarted()) {return;}//意思是:如果我們沒有設置viewName,就采用默認的。否則采用我們自己的applyDefaultViewName(processedRequest, mv);//步驟5,執行攔截器post方法,重要// 執行所有的攔截器的postHandle方法,并且把mv給他// 這里有一個小細節:這個時候攔截器是【倒序】執行的mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}//步驟6,處理視圖,重要//這個方法很重要,顧名思義,他是來處理結果的,渲染視圖、處理異常等等的 下面詳細分解processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {//步驟7,執行攔截器收尾方法,重要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接口中三個方法的執行順序:
五、例子
當全部攔截器的preHandle返回值都是true時,調用攔截器返回如下:
當C的preHandle返回false時,返回如下:
總結
以上是生活随笔為你收集整理的SpringMVC拦截器HandlerInterceptor原理及使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SaaS的行业概述及发展现状
- 下一篇: 2020年短视频用户价值研究报告