深入源碼分析SpringMVC執(zhí)行過(guò)程

點(diǎn)擊上方“武培軒”,選擇“設(shè)為星標(biāo)”
技術(shù)文章第一時(shí)間送達(dá)!
本文主要講解 SpringMVC 執(zhí)行過(guò)程,并針對(duì)相關(guān)源碼進(jìn)行解析。
首先,讓我們從 Spring MVC 的四大組件:前端控制器(DispatcherServlet)、處理器映射器(HandlerMapping)、處理器適配器(HandlerAdapter)以及視圖解析器(ViewResolver) 的角度來(lái)看一下 Spring MVC 對(duì)用戶請(qǐng)求的處理過(guò)程,過(guò)程如下圖所示:
SpringMVC 執(zhí)行過(guò)程- 用戶請(qǐng)求發(fā)送到前端控制器 DispatcherServlet。
- 前端控制器 DispatcherServlet 接收到請(qǐng)求后,DispatcherServlet 會(huì)使用 HandlerMapping 來(lái)處理,HandlerMapping 會(huì)查找到具體進(jìn)行處理請(qǐng)求的 Handler 對(duì)象。
- HandlerMapping 找到對(duì)應(yīng)的 Handler 之后,并不是返回一個(gè) Handler 原始對(duì)象,而是一個(gè) Handler 執(zhí)行鏈(HandlerExecutionChain),在這個(gè)執(zhí)行鏈中包括了攔截器和處理請(qǐng)求的 Handler。HandlerMapping 返回一個(gè)執(zhí)行鏈給 DispatcherServlet。
- DispatcherServlet 接收到執(zhí)行鏈之后,會(huì)調(diào)用 Handler 適配器去執(zhí)行 Handler。
- HandlerAdapter執(zhí)行完成 Handler之后會(huì)得到一個(gè) ModelAndView,并返回給 DispatcherServlet。
- DispatcherServlet 接收到 HandlerAdapter 返回的 ModelAndView 之后,會(huì)根據(jù)其中的視圖名調(diào)用 ViewResolver。
- ViewResolver 根據(jù)邏輯視圖名解析成一個(gè)真正的 View 視圖,并返回給 DispatcherServlet。
- DispatcherServlet 接收到視圖之后,會(huì)根據(jù)上面的 ModelAndView 中的 model 來(lái)進(jìn)行視圖中數(shù)據(jù)的填充,也就是所謂的視圖渲染。
- 渲染完成之后,DispatcherServlet 就可以將結(jié)果返回給用戶了。
在了解了大概的執(zhí)行過(guò)程后,讓我們一起去從源碼角度去深入探索(SpringMVC 版本為 5.2.3):
我們先創(chuàng)建一個(gè) Controller 以便進(jìn)行 debug,內(nèi)容如下:
public class SpringMvcController {("testSpringMvc")public String testSpringMvc(Map<String, String> map) {map.put("note", "在看轉(zhuǎn)發(fā)二連");return "success";}}
然后再創(chuàng)建一個(gè) html 文件,采用 Thymeleaf 模版引擎,文件內(nèi)容如下:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"><head><meta charset="UTF-8"><title>在看轉(zhuǎn)發(fā)二連title>head><body><h1 th:text="${note}">h1>body>html>
好了,然后啟動(dòng)程序,讓我們?cè)L問(wèn) http://localhost:8080/testSpringMvc,來(lái)一步一步探索 SpringMVC 的執(zhí)行過(guò)程:
首先當(dāng)我們?cè)L問(wèn)頁(yè)面的時(shí)候,將會(huì)把請(qǐng)求發(fā)送到前端控制器 DispatcherServlet,DispatcherServlet 是一個(gè) Servlet,我們知道在 Servlet 在處理一個(gè)請(qǐng)求的時(shí)候會(huì)交給 service 方法進(jìn)行處理,這里也不例外,DispatcherServlet 繼承了 FrameworkServlet,首先進(jìn)入 FrameworkServlet 的 service 方法:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 請(qǐng)求方法HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());// 若方法為 PATCH 方法或?yàn)榭談t單獨(dú)處理if (httpMethod == HttpMethod.PATCH || httpMethod == null) {processRequest(request, response);}else {// 其他的請(qǐng)求類型的方法經(jīng)由父類,也就是 HttpServlet 處理super.service(request, response);}}
HttpServlet 中會(huì)根據(jù)請(qǐng)求類型的不同分別調(diào)用 doGet 或者 doPost 等方法,F(xiàn)rameworkServlet 中已經(jīng)重寫(xiě)了這些方法,在這些方法中會(huì)調(diào)用 processRequest 進(jìn)行處理,在 processRequest 中會(huì)調(diào)用 doService 方法,這個(gè) doService 方法就是在 DispatcherServlet 中實(shí)現(xiàn)的。下面就看下 DispatcherServlet 中的 doService 方法的實(shí)現(xiàn)。
DispatcherServlet 收到請(qǐng)求
DispatcherServlet 中的 doService方法:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {logRequest(request);// 給 request 中的屬性做一份快照,以便能夠恢復(fù)原始屬性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));}}}// 如果沒(méi)有配置本地化或者主題的處理器之類的,SpringMVC 會(huì)使用默認(rèn)的配置文件,即 DispatcherServlet.propertiesrequest.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 {// 開(kāi)始真正的處理doDispatch(request, response);}finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// 恢復(fù)原始屬性快照if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}}}
接下來(lái) DispatcherServlet 開(kāi)始真正的處理,讓我們來(lái)看下 doDispatch 方法,首先會(huì)獲取當(dāng)前請(qǐng)求的 Handler 執(zhí)行鏈,然后找到合適的 HandlerAdapter,接著調(diào)用 RequestMappingHandlerAdapter 的 handle 方法,如下為 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 類型的,比如上傳等;如果是 Multipart 類型的,則轉(zhuǎn)換為 MultipartHttpServletRequest 類型processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 獲取當(dāng)前請(qǐng)求的 Handler 執(zhí)行鏈mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 獲取當(dāng)前請(qǐng)求的 Handler 適配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 對(duì)于 header 中 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;}}// 遍歷所有定義的 interceptor,執(zhí)行 preHandle 方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 實(shí)際調(diào)用 Handler 的地方mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}// 處理成默認(rèn)視圖名,也就是添加前綴和后綴等applyDefaultViewName(processedRequest, mv);// 攔截器postHandle方法進(jìn)行處理mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}// 處理最后的結(jié)果,渲染之類的都在這里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()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}
查找對(duì)應(yīng)的 Handler 對(duì)象
讓我們?nèi)ヌ剿飨率侨绾潍@取當(dāng)前請(qǐng)求的 Handler 執(zhí)行鏈,對(duì)應(yīng)著這句代碼 mappedHandler = getHandler(processedRequest);,看下 DispatcherServlet 具體的 getHandler 方法,該方法主要是遍歷所有的 handlerMappings 進(jìn)行處理,handlerMappings 是在啟動(dòng)的時(shí)候預(yù)先注冊(cè)好的,在循環(huán)中會(huì)調(diào)用 AbstractHandlerMapping 類中的 getHandler 方法來(lái)獲取 Handler 執(zhí)行鏈,若獲取的 Handler 執(zhí)行鏈不為 null,則返回當(dāng)前請(qǐng)求的 Handler 執(zhí)行鏈,DispatcherServlet 類的 getHandler 方法如下:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {// 遍歷所有的 handlerMappings 進(jìn)行處理,handlerMappings 是在啟動(dòng)的時(shí)候預(yù)先注冊(cè)好的for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}
在循環(huán)中,根據(jù) mapping.getHandler(request);,繼續(xù)往下看 AbstractHandlerMapping 類中的 getHandler 方法:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 根據(jù) request 獲取 handlerObject handler = getHandlerInternal(request);if (handler == null) {// 如果沒(méi)有找到就使用默認(rèn)的 handlerhandler = getDefaultHandler();}if (handler == null) {return null;}// 如果 Handler 是 String,表明是一個(gè) bean 名稱,需要尋找對(duì)應(yīng) beanif (handler instanceof String) {String handlerName = (String) handler;handler = obtainApplicationContext().getBean(handlerName);}// 封裝 Handler 執(zhí)行鏈return getHandlerExecutionChain(handler, request);}
AbstractHandlerMapping 類中的 getHandler 方法中首先根據(jù) requrst 獲取 handler,主要是調(diào)用了 AbstractHandlerMethodMapping 類中的 getHandlerInternal 方法,該方法首先獲取 request 中的 url,即 /testSpringMvc,用來(lái)匹配 handler 并封裝成 HandlerMethod,然后根據(jù) handlerMethod 中的 bean 來(lái)實(shí)例化 Handler 并返回。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {// 獲取 request 中的 url,用來(lái)匹配 handlerString lookupPath = getUrlPathHelper().getLookupPathForRequest(request);request.setAttribute(LOOKUP_PATH, lookupPath);this.mappingRegistry.acquireReadLock();try {// 根據(jù)路徑尋找 Handler,并封裝成 HandlerMethodHandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);// 根據(jù) handlerMethod 中的 bean 來(lái)實(shí)例化 Handler,并添加進(jìn) HandlerMethodreturn (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}finally {this.mappingRegistry.releaseReadLock();}}
接下來(lái),我們看 lookupHandlerMethod 的邏輯,主要邏輯委托給了 mappingRegistry 這個(gè)成員變量來(lái)處理:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {Listmatches = new ArrayList<>(); // 通過(guò) lookupPath 屬性中查找。如果找到了,就返回對(duì)應(yīng)的RequestMappingInfoListdirectPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) {// 如果匹配到了,檢查其他屬性是否符合要求,如請(qǐng)求方法,參數(shù),header 等addMatchingMappings(directPathMatches, matches, request);}if (matches.isEmpty()) {// 沒(méi)有直接匹配到,則遍歷所有的處理方法進(jìn)行通配符匹配addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);}if (!matches.isEmpty()) {// 如果方法有多個(gè)匹配,不同的通配符等,則排序選擇出最合適的一個(gè)Comparatorcomparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator);Match bestMatch = matches.get(0);// 如果有多個(gè)匹配的,會(huì)找到第二個(gè)最合適的進(jìn)行比較if (matches.size() > 1) {if (logger.isTraceEnabled()) {logger.trace(matches.size() + " matching mappings: " + matches);}if (CorsUtils.isPreFlightRequest(request)) {return PREFLIGHT_AMBIGUOUS_MATCH;}Match secondBestMatch = matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {Method m1 = bestMatch.handlerMethod.getMethod();Method m2 = secondBestMatch.handlerMethod.getMethod();String uri = request.getRequestURI();// 不能有相同的最優(yōu) Matchthrow new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");}}request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);// 設(shè)置 request 參數(shù)(RequestMappingHandlerMapping 對(duì)其進(jìn)行了覆寫(xiě))handleMatch(bestMatch.mapping, lookupPath, request);// 返回匹配的 url 的處理的方法return bestMatch.handlerMethod;}else {// 調(diào)用 RequestMappingHandlerMapping 類的 handleNoMatch 方法再匹配一次return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);}}
通過(guò)上面的過(guò)程,我們就獲取到了 Handler,就開(kāi)始封裝執(zhí)行鏈了,就是將我們配置的攔截器加入到執(zhí)行鏈中去,getHandlerExecutionChain 方法如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {// 如果當(dāng)前 Handler 不是執(zhí)行鏈類型,就使用一個(gè)新的執(zhí)行鏈實(shí)例封裝起來(lái)HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);// 遍歷攔截器,找到跟當(dāng)前 url 對(duì)應(yīng)的,添加進(jìn)執(zhí)行鏈中去for (HandlerInterceptor interceptor : this.adaptedInterceptors) {if (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}else {chain.addInterceptor(interceptor);}}return chain;}
到此為止,我們就獲取了當(dāng)前請(qǐng)求的 Handler 執(zhí)行鏈,接下來(lái)看下是如何獲取請(qǐng)求的 Handler 適配器,主要依靠 DispatcherServlet 類的 getHandlerAdapter 方法,該方法就是遍歷所有的 HandlerAdapter,找到和當(dāng)前 Handler 匹配的就返回,在這里匹配到的為 RequestMappingHandlerAdapter。
DispatcherServlet 類的 getHandlerAdapter 方法如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {// 遍歷所有的 HandlerAdapter,找到和當(dāng)前 Handler 匹配的就返回for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");}
HandlerAdapter 執(zhí)行當(dāng)前的 Handler
再獲取完當(dāng)前請(qǐng)求的 Handler 適配器后,接著進(jìn)行緩存處理,也就是對(duì) last-modified 的處理,然后調(diào)用 applyPreHandle 方法執(zhí)行攔截器的 preHandle 方法,即遍歷所有定義的 interceptor,執(zhí)行 postHandle 方法,然后就到了實(shí)際執(zhí)行 handle 的地方,doDispatch 方法中 handle 方法是執(zhí)行當(dāng)前 Handler,我們這里使用的是 RequestMappingHandlerAdapter,首先會(huì)進(jìn)入 AbstractHandlerMethodAdapter 的 handle 方法:
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}
在 AbstractHandlerMethodAdapter 的 handle 方法中又調(diào)用了 RequestMappingHandlerAdapter 類的 handleInternal 方法:
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;checkRequest(request);if (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// 執(zhí)行方法,封裝 ModelAndViewmav = invokeHandlerMethod(request, response, handlerMethod);}if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav;}
在執(zhí)行完 handle 方法后,然后調(diào)用 applyDefaultViewName 方法組裝默認(rèn)視圖名稱,將前綴和后綴名都加上,接著調(diào)用 applyPostHandle 方法執(zhí)行攔截器的 preHandle 方法,也就是遍歷所有定義的 interceptor,執(zhí)行postHandle 方法。
處理最終結(jié)果以及渲染
最終調(diào)用 DispatcherServlet 類中的 processDispatchResult 方法,此方法主要是處理最終結(jié)果的,包括異常處理、渲染頁(yè)面和發(fā)出完成通知觸發(fā)攔截器的 afterCompletion() 方法執(zhí)行等。processDispatchResult()方法代碼如下:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}if (mv != null && !mv.wasCleared()) {// 渲染render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {return;}if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}}
接下來(lái)讓我們看下 DispatcherServlet 類的 render 方法是如何完成渲染的,DispatcherServlet 類的 render 方法渲染過(guò)程如下:
- 判斷 ModelAndView 中 view 是否為 view name,沒(méi)有獲取其實(shí)例對(duì)象:如果是根據(jù) name,如果是則需要調(diào)用 resolveViewName 從視圖解析器獲取對(duì)應(yīng)的視圖(View)對(duì)象;否則 ModelAndView 中使用 getview 方法獲取 view 對(duì)象。
- 然后調(diào)用 View 類的 render 方法。
DispatcherServlet 類的 render 方法如下:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {// 設(shè)置本地化Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());response.setLocale(locale);View view;String viewName = mv.getViewName();if (viewName != null) {// 解析視圖名,得到視圖view = resolveViewName(viewName, mv.getModelInternal(), locale, request);if (view == null) {throw new ServletException("Could not resolve view with name '" + mv.getViewName() +"' in servlet with name '" + getServletName() + "'");}}else {view = mv.getView();if (view == null) {throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +"View object in servlet with name '" + getServletName() + "'");}}if (logger.isTraceEnabled()) {logger.trace("Rendering view [" + view + "] ");}try {if (mv.getStatus() != null) {response.setStatus(mv.getStatus().value());}// 委托給視圖進(jìn)行渲染view.render(mv.getModelInternal(), request, response);}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug("Error rendering view [" + view + "]", ex);}throw ex;}}
因?yàn)槲覀冇玫氖?Thymeleaf 模版引擎,所以 view.render 找到對(duì)應(yīng)的視圖 ThymeleafView 的 render 方法進(jìn)行渲染。
public void render(final Mapmodel, final HttpServletRequest request, final HttpServletResponse response) throws Exception {renderFragment(this.markupSelectors, model, request, response);}
ThymeleafView 的 render 方法又調(diào)用 renderFragment 方法進(jìn)行視圖渲染,渲染完成之后,DispatcherServlet 就可以將結(jié)果返回給我們了。
也就是 note 對(duì)應(yīng)的 value:在看轉(zhuǎn)發(fā)二連。
總結(jié)通過(guò)本文的源碼分析,我相信我們都能夠清楚的認(rèn)識(shí)到 SpringMVC 執(zhí)行流程,進(jìn)一步加深對(duì) SpringMVC 的理解。
歡迎大家點(diǎn)擊小程序留言討論(可以上傳圖片)。
參考
https://dwz.cn/DmzEGQcx
https://dwz.cn/MSB2GJKT
完
? ? ? ?
???●答完這10道題,我哭了(內(nèi)含答案解析)●實(shí)現(xiàn)線程的方式到底有幾種?●你真的了解 volatile 關(guān)鍵字嗎?
