SpringMVC中的DispatcherServlet請求分析
一、service請求(servlet請求轉換為Http請求)
DispatcherServlet作為一個Servlet,那么當有請求到Tomcat等Servlet服務器時,會調用其service方法。再調用到其父類GenericServlet的service方法,HttpServlet中實現(xiàn),如下(開始請求的調用):
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException(lStrings.getString("http.non_http")); } service(request, response); }
HttpServlet層:將request和response類型進行轉換后,繼續(xù)調用service方法:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { // Invalid date header - proceed as if none was set ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
根據(jù)調用的Http請求的方式,調用具體的底層(FrameworkServlet層)方法,get、post請求等都會有相同的處理,比如doGet如下:
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); publishRequestHandledEvent(request, response, startTime, failureCause); } }
主要的核心邏輯為doService,但是Tomcat是使用線程池的方式接受來自客戶端的請求的,當前請求中可能帶有Locate(國際化參數(shù)信息),那么需要使用ThreadLocal在請求前記錄參數(shù)信息,在請求之后finally中將參數(shù)恢復回去,不會影響到下一個請求。
Spring經(jīng)常會這樣進行處理,比如AopContext等處理Aop切面信息。
二、doService請求(request中添加SpringMVC初始化的九大件信息)
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request); // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } try { doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
1、打印請求日志
2、請求中添加屬性(WebApplicationContext容器,i18n解析器,主題解析器,主題,重定向屬性處理)
3、核心方法 doDispatch
三、doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; // 異步請求屬性解析(重定向) WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 如果是Multipart上傳文件請求,則調用multipartResolver.resolveMultipart(上傳文件解析器進行解析) processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 確定Handler處理該請求(HandlerExecutionChain調用鏈,責任鏈模式) mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根據(jù)初始化時加載的適配器挨個匹配是否能適配該調用鏈 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 對請求頭中的last-modified進行處理 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; } } // 對HandlerExecutionChain中的所有HandlerInterceptor調用preHandle方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 真正的請求調用 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 判斷Controller返回的ModelAndView中是否有View, // 沒有則使用viewNameTranslator執(zhí)行沒有試圖的解析規(guī)則 applyDefaultViewName(processedRequest, mv); // 對HandlerExecutionChain中的所有HandlerInterceptor調用postHandle方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } // 處理結果解析(ModelAndView或者Exception),保證最終會執(zhí)行所以Interceptor的triggerAfterCompletion processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // 不知道什么情況下回進入相當于執(zhí)行所以Interceptor的postHandle和afterCompletion方法 if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // 清除當前文件上傳請求的資源 if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
這里是整個SpringMVC的核心,每個流程都可能會比較復雜,后續(xù)單獨分析。流程如下(主要是標紅的步驟):
1、異步請求屬性解析(重定向)
2、如果是Multipart上傳文件請求,則調用multipartResolver.resolveMultipart(上傳文件解析器進行解析)
4、確定Handler處理該請求(HandlerExecutionChain調用鏈,責任鏈模式)
5、根據(jù)初始化時加載的適配器挨個匹配是否能適配該調用鏈
6、對請求頭中的last-modified進行處理
7、對HandlerExecutionChain中的所有HandlerInterceptor調用preHandle方法
8、真正的請求調用
9、沒有試圖的解析返回
10、對HandlerExecutionChain中的所有HandlerInterceptor調用postHandle方法
11、處理結果解析(ModelAndView或者Exception),保證最終會執(zhí)行所以Interceptor的triggerAfterCompletion
12、清除當前文件上傳請求的資源
HandlerInterceptor方法調用
當我們需要使用Spring的攔截器時,會集實現(xiàn)HandlerInterceptor的preHandle、postHandle、triggerAfterCompletion方法。
當?shù)?步完成后我們獲取到了HandlerExecutionChain調用鏈,其中包括需要執(zhí)行的攔截器和真正調用的方法(后面專門分析專門獲取的)。
但是在上面的步驟看到了對攔截器的三個方法的調用時機,其實每個調用的方法都差不多,就看preHandle方法即可,調用HandlerExecutionChain的applyPreHandle方法如下:
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; }
由于preHandle方法允許返回false則不執(zhí)行真實的Controller方法調用,所以需要每次判斷。但是在執(zhí)行preHandle和postHandle方法時,都允許最后調用一次triggerAfterCompletion方法。都是在拿到調用鏈中的所以有序的攔截器,輪訓調用其對應的方法即可。
到此這篇關于SpringMVC中的DispatcherServlet請求分析的文章就介紹到這了,更多相關DispatcherServlet請求分析內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于java ssm springboot實現(xiàn)選課推薦交流平臺系統(tǒng)
這篇文章主要介紹了選課推薦交流平臺系統(tǒng)是基于java ssm springboot來的實現(xiàn)的,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08解讀@ConfigurationProperties使用時的幾個常見誤區(qū)
在Spring Boot中,@ConfigurationProperties注解用于綁定配置文件中的屬性到Java對象,它支持properties和yml文件格式,并且可以通過prefix屬性指定配置屬性的前綴,需要注意的是,@PropertySource注解默認只支持properties文件,不支持yml文件2024-10-10Springboot Thymeleaf實現(xiàn)HTML屬性設置
這篇文章主要介紹了Springboot Thymeleaf實現(xiàn)HTML屬性設置,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2007-11-11