天天看點

啷個說明白:SpringMVC到底是怎麼個工作流程啊?

作者:尚矽谷教育

01 SpringMVC簡介

SpringMVC是一種基于Spring實作了Web MVC設計模式的請求驅動類型的輕量級Web架構,使用了MVC架構模式的思想,将web層進行職責解耦,并管理應用所需對象的生命周期,為簡化日常開發,提供了很大便利。

SpringMVC提供的核心元件包括請求分發器(DispatchServlet)、請求處理映射器(HandlerMapping)、請求處理擴充卡(Handler Adapter)、視圖解析器(View Resolver) 、動作處理器(Controller)和模型視圖對象(ModelAndView),通過以上各大元件的協同工作來完成用戶端的請求響應工作,使得MVC架構的設計理念得以實作。

02 SpringMVC工作流程

我們先來看一下SpringMVC的流程圖

啷個說明白:SpringMVC到底是怎麼個工作流程啊?

(1)用戶端向web伺服器(如tomcat)發送一個http請求,web伺服器對http請求進行解析,解析後的url位址如果比對到DispatchServlet的映射路徑(通過web.xml中的servlet-mapping配置),web容器就會将請求交給DispatchServlet處理

(2)DispatcherServlet接收到這個請求後,再對URL進行解析,得到請求資源辨別符(URI)。然後調用相應方法得到的HandlerMapping對象,再根據URI,調用這個對象的相應方法獲得Handler對象以及它對應的攔截器。(在這裡隻是獲得了Handler對象,并不會操作它,在SpringMVC中,是通過HandlerAdapter對Handler進行調用、控制的)

(3)DispatcherServlet根據得到的Handler對象,選擇一個合适的HandlerAdapter,建立其執行個體對象,執行攔截器中的preHandler()方法。

(4)在攔截器方法中,提取請求中的資料模型,填充Handler入參,是以所有準備工作都已做好,開始執行Handler(我們寫的controller代碼并不是能被直接執行,需要有剛才那些操作,才能轉變為Handler被執行)。

(5)Handler執行完畢後傳回一個ModelAndView對象給DispatcherServlet。

(6)這個ModleAndView隻是一個邏輯視圖,并不是真正的視圖,DispatcherServlet通過ViewResolver視圖解析器将邏輯視圖轉化為真正的視圖(通俗了解為将視圖名稱補全,如加上路徑字首,加上.jsp字尾,能指向實際的視圖)。

(7)DispatcherServlet通過Model将ModelAndView中得到的處資料解析後用于渲染視圖。将得到的最終視圖通過http響應傳回用戶端。

03 SpringMVC元件解析

Part.01 HandlerMapping

該元件在SpringMVC體系結構中有着舉足輕重的地位,充當着url和Controller之間映射關系配置的角色。主要有三部分組成:HandlerMapping映射注冊、根據url擷取對應的處理器、攔截器注冊。

啷個說明白:SpringMVC到底是怎麼個工作流程啊?

在Spring MVC中,關于HandlerMapping的使用,主要包括兩個部分:注冊和查找。在HandlerMapping的實作中,持有一個handlerMap這樣一個HashMap<String, Object>,其中key是http請求的path資訊,value可以是一個字元串,或者是一個處理請求的HandlerExecutionChain,如果是String類型,則會将其視為Spring的bean名稱。在HandlerMapping對象的建立中,IoC容器執行了一個容器回調方法setApplicationContext,在這個方法中調用initApplicationContext方法進行初始化,各個子類可以根據需求的不同覆寫這個方法。關于handlerMap資訊的注冊就是在initApplicationContext方法中被執行的。

Part.02 HandlerAdapter

該元件在SpringMVC請求執行過程中包括兩個部分,HandlerAdapter的注冊和HandlerAdapter的執行。

啷個說明白:SpringMVC到底是怎麼個工作流程啊?

(1)HandlerAdapter的注冊:

DispatcherServlte會根據配置檔案資訊注冊HandlerAdapter,如果在配置檔案中沒有配置,那麼DispatcherServlte會擷取HandlerAdapter的預設配置,如果是讀取預設配置的話,DispatcherServlte會讀取DispatcherServlte.properties檔案,該檔案中配置了三種HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。DispatcherServlte會将這三個HandlerAdapter對象存儲到它的handlerAdapters這個集合屬性中,這樣就完成了HandlerAdapter的注冊。

(2)HandlerAdapter的執行:

DispatcherServlte會根據handlerMapping傳過來的controller與已經注冊好了的HandlerAdapter一一比對,看哪一種HandlerAdapter是支援該controller類型的,如果找到了其中一種HandlerAdapter是支援傳過來的controller類型,那麼該HandlerAdapter會調用自己的handle方法,handle方法運用java的反射機制執行controller的具體方法來獲得ModelAndView,例如SimpleControllerHandlerAdapter是支援實作了controller接口的控制器,如果自己寫的控制器實作了controller接口,那麼SimpleControllerHandlerAdapter就會去執行自己寫控制器中的具體方法來完成請求。

04 SpringMVC源碼簡單剖析

根據上邊的圖我們可以看到前端控制器在springMVC的系統中擁有了非常大的作用,那麼我們就以此來入手。

啷個說明白:SpringMVC到底是怎麼個工作流程啊?
  • 首先在它的關系圖中我們可以看出,DispatcherServlet類繼承FrameworkServlet類,而FrameworkServlet繼承了HttpServletBean類,再往上就是HttpServlet類與Servlet類了。
  • 那麼我們就可以得出DispatchServlet是一個servlet類,既然是一個Servlet那麼就有一個核心方法:Service()方法,我們在DispatchServlet中找,發現沒有,那我們繼續去它的父類中找,于是發現了如下方法:

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());

if (HttpMethod.PATCH != httpMethod && httpMethod != null) {

super.service(request, response);

} else {

this.processRequest(request, response);

}

}

  • 它首先判斷是不是PATCH請求,如果是的話執行父類的service()方法,如果不是執行 processRequest(),那我們繼續追蹤:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

long startTime = System.currentTimeMillis();

Throwable failureCause = null;

LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();

LocaleContext localeContext = this.buildLocaleContext(request);

RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();

ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));

this.initContextHolders(request, localeContext, requestAttributes);

try {

this.doService(request, response);

} catch (IOException | ServletException var16) {

failureCause = var16;

throw var16;

} catch (Throwable var17) {

failureCause = var17;

throw new NestedServletException("Request processing failed", var17);

} finally {

this.resetContextHolders(request, previousLocaleContext, previousAttributes);

if (requestAttributes != null) {

requestAttributes.requestCompleted();

}

if (this.logger.isDebugEnabled()) {

if (failureCause != null) {

this.logger.debug("Could not complete request", (Throwable)failureCause);

} else if (asyncManager.isConcurrentHandlingStarted()) {

this.logger.debug("Leaving response open for concurrent processing");

} else {

this.logger.debug("Successfully completed request");

}

}

this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);

}

}

  • 這裡邊的代碼主要是執行一些控制器等等,可以不用了解太多,我們主要是了解其執行流程。在其中我們可以看到 this.doService(request, response);方法,來執行一些事務,那麼我們繼續追蹤:

protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

  • 我們發現它是調用了子類:DispatcherServlet類中的方法,繼續追蹤:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

if (this.logger.isDebugEnabled()) {

String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";

this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");

}

Map<String, Object> attributesSnapshot = null;

if (WebUtils.isIncludeRequest(request)) {

attributesSnapshot = new HashMap();

Enumeration attrNames = request.getAttributeNames();

label112:

while(true) {

String attrName;

do {

if (!attrNames.hasMoreElements()) {

break label112;

}

attrName = (String)attrNames.nextElement();

} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

attributesSnapshot.put(attrName, request.getAttribute(attrName));

}

}

request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());

request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);

request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);

request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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 {

this.doDispatch(request, response);

} finally {

if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {

this.restoreAttributesAfterInclude(request, attributesSnapshot);

}

}

}

  • 我們看到它首先設定了一些request屬性,然後執行了一個this.doDispatch(request, response)方法,至此我們算是找到了它的核心執行代碼:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

HttpServletRequest processedRequest = request;

HandlerExecutionChain mappedHandler = null;

boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {

try {

ModelAndView mv = null;

Object dispatchException = null;

try {

//這個方法下面有單獨說明

processedRequest = this.checkMultipart(request);

multipartRequestParsed = processedRequest != request;

//這個方法下面有單獨說明

mappedHandler = this.getHandler(processedRequest);

if (mappedHandler == null) {

this.noHandlerFound(processedRequest, response);

return;

}

//這個方法下面有單獨說明

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

String method = request.getMethod();

boolean isGet = "GET".equals(method);

if (isGet || "HEAD".equals(method)) {

long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

if (this.logger.isDebugEnabled()) {

this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);

}

if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {

return;

}

}

//這個方法下面有單獨說明

if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;

}

//這個方法下面有單獨說明

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

this.applyDefaultViewName(processedRequest, mv);

//這個方法下面有單獨說明

mappedHandler.applyPostHandle(processedRequest, response, mv);

} catch (Exception var20) {

dispatchException = var20;

} catch (Throwable var21) {

dispatchException = new NestedServletException("Handler dispatch failed", var21);

}

//這個方法下面有單獨說明

this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

} catch (Exception var22) {

this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);

} catch (Throwable var23) {

this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));

}

} finally {

if (asyncManager.isConcurrentHandlingStarted()) {

if (mappedHandler != null) {

mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);

}

} else if (multipartRequestParsed) {

this.cleanupMultipart(processedRequest);

}

}

}

  • 這段代碼可以分成四個部分:
  • ①查找Handler對應的HandlerMapping部分:

try {

processedRequest = this.checkMultipart(request);

multipartRequestParsed = processedRequest != request;

mappedHandler = this.getHandler(processedRequest);

if (mappedHandler == null) {

this.noHandlerFound(processedRequest, response);

return;

}

  • 它首先判斷request請求是不是二進制請求,然後執行this.getHandler(processedRequest);方法。那麼我們來了解一下這個方法:
  • ②this.getHandler(processedRequest)部分

@Nullable

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

if (this.handlerMappings != null) {

Iterator var2 = this.handlerMappings.iterator();

while(var2.hasNext()) {

HandlerMapping hm = (HandlerMapping)var2.next();

if (this.logger.isTraceEnabled()) {

this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");

}

HandlerExecutionChain handler = hm.getHandler(request);

if (handler != null) {

return handler;

}

}

}

return null;

}

  • 首先:我們發現它擷取了一個HandlerMapping的疊代器,然後查找每一個HandlerMapping。那麼它為什麼要先去查找HandlerMapping呢?那是因為我們在配置Controller的時候有兩種方式:xml和注解的方式,是以它先去找到HandlerMapping,然後根據HandlerMapping去找Handler,就是hm.getHandler(request);這個方法來查找Handler。
  • ③查找HandlerAdapter部分:

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

String method = request.getMethod();

boolean isGet = "GET".equals(method);

if (isGet || "HEAD".equals(method)) {

long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

if (this.logger.isDebugEnabled()) {

this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);

}

if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {

return;

}

}

④在拿到HandlerMapping之後,開始查找HandlerAdapter,就是ha.getLastModified(request, mappedHandler.getHandler());來查找。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

if (this.handlerAdapters != null) {

Iterator var2 = this.handlerAdapters.iterator();

while(var2.hasNext()) {

HandlerAdapter ha = (HandlerAdapter)var2.next();

if (this.logger.isTraceEnabled()) {

this.logger.trace("Testing handler adapter [" + ha + "]");

}

if (ha.supports(handler)) {

return ha;

}

}

}

throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");

}

它的查找方式與之前查找HandlerMapping類似。

  • 攔截器執行部分:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;

}

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {

HandlerInterceptor[] interceptors = this.getInterceptors();

if (!ObjectUtils.isEmpty(interceptors)) {

for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {

HandlerInterceptor interceptor = interceptors[i];

if (!interceptor.preHandle(request, response, this.handler)) {

this.triggerAfterCompletion(request, response, (Exception)null);

return false;

}

}

}

return true;

}

以上一小段代碼就是擷取攔截器然後執行。

  • 再接下來就是最重要的Hanler執行部分,它傳回的對象是mv,就是ModelAndView對象

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

this.applyDefaultViewName(processedRequest, mv);

mappedHandler.applyPostHandle(processedRequest, response, mv);

} catch (Exception var20) {

dispatchException = var20;

} catch (Throwable var21) {

dispatchException = new NestedServletException("Handler dispatch failed", var21);

}

//這個方法下面有說明

this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

它首先在 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 執行了handler并傳回了一個視圖;然後通過 this.applyDefaultViewName(processedRequest, mv); 在我們沒有設定視圖名稱的時候傳回預設視圖名;然後通過 this.applyDefaultViewName(processedRequest, mv); 擷取到項目的攔截器,然後判斷攔截器是否存在,存在則對該請求進行測試(postHandler),如果有一個攔截器不成功,則請求失敗。最後來到視圖解析和渲染的執行方法:this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {

boolean errorView = false;

if (exception != null) {

if (exception instanceof ModelAndViewDefiningException) {

this.logger.debug("ModelAndViewDefiningException encountered", exception);

mv = ((ModelAndViewDefiningException)exception).getModelAndView();

} else {

Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;

mv = this.processHandlerException(request, response, handler, exception);

errorView = mv != null;

}

}

if (mv != null && !mv.wasCleared()) {

//這個方法下面有說明

this.render(mv, request, response);

if (errorView) {

WebUtils.clearErrorRequestAttributes(request);

}

} else if (this.logger.isDebugEnabled()) {

this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");

}

if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

if (mappedHandler != null) {

mappedHandler.triggerAfterCompletion(request, response, (Exception)null);

}

}

}

它首先判斷一下是否異常,如果出現異常,判斷異常是否是ModelAndViewDefiningException的執行個體,如果是就直接調用getModelAndView方法傳回一個mv,否則調用processHanlderException方法初始化mv對象;如果沒有異常出現,那麼判斷mv是否為空,為空抛出異常,不為空則判斷mv,wasCleared()方法傳回的值是否為真;判斷完成以後,執行了 this.render(mv, request, response);方法。

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {

Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();

response.setLocale(locale);

String viewName = mv.getViewName();

View view;

if (viewName != null) {

view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);

if (view == null) {

throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.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 '" + this.getServletName() + "'");

}

}

if (this.logger.isDebugEnabled()) {

this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");

}

try {

if (mv.getStatus() != null) {

response.setStatus(mv.getStatus().value());

}

view.render(mv.getModelInternal(), request, response);

} catch (Exception var8) {

if (this.logger.isDebugEnabled()) {

this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var8);

}

throw var8;

}

}

它首先設定了視圖的編碼格式,然後通過 view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);進行了視圖解析:

public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

if (this.logger.isTraceEnabled()) {

this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes);

}

Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);

this.prepareResponse(request, response);

this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);

}

它首先通過 Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);擷取了傳入的model的map集合。然後通過 this.prepareResponse(request, response);設定響應頭的兩個參數,最後通過 this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);将mergeModel合到請求裡.

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

this.exposeModelAsRequestAttributes(model, request);

this.exposeHelpers(request);

String dispatcherPath = this.prepareForRendering(request, response);

RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);

if (rd == null) {

throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");

} else {

if (this.useInclude(request, response)) {

response.setContentType(this.getContentType());

if (this.logger.isDebugEnabled()) {

this.logger.debug("Including resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");

}

rd.include(request, response);

} else {

if (this.logger.isDebugEnabled()) {

this.logger.debug("Forwarding to resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");

}

rd.forward(request, response);

}

}

}

它首先進行了 this.exposeModelAsRequestAttributes(model, request);操作:

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {

model.forEach((modelName, modelValue) -> {

if (modelValue != null) {

request.setAttribute(modelName, modelValue);

if (this.logger.isDebugEnabled()) {

this.logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + this.getBeanName() + "'");

}

} else {

request.removeAttribute(modelName);

if (this.logger.isDebugEnabled()) {

this.logger.debug("Removed model object '" + modelName + "' from request in view with name '" + this.getBeanName() + "'");

}

}

});

}

由此可見,model中的資料,最終是放在request域中的。

然後他們進行了RequestDispatcher 對象配置,就是選擇它要進行的操作了,是使用include還是forward進行跳轉。至此,視圖的解析與渲染工作都完成了,并且也發送到了前端頁面,完成了一個請求的整個流程。

05 總結

SpringMVC的總體執行流程的源碼解析就為以上描述,我們發現SpringMVC最主要的控制器類DispatcherServlet,它在項目的web.xml中配置攔截位址。當我們發送請求時就可以進入到以上代碼中,進而完成整個請求處理。