SpringMVC源碼分析之DispatcherServlet的執行流程

SpringMVC源碼分析之DispatcherServlet

1.DispatcherServlet的初始化

​ 前面一片文章大概的講述了DispatcherServlet的基本作用,也知道它其實就是一個Servlet,下面詳細分析DispatcherServlet。

​ Web容器初始化後,會爲每個Servlet創建對象,並加載其上下文環境。我們都知道,在Tomcat容器中,維護了一個線程池,每當有一個客戶請求抵達服務器的時候,就分配一個線程來處理該請求。同時,Servlet是單例多線程的,也就是多個請求共用一個Servlet容器。所以呢,DispatcherServlet的完整定義是:是一個具有唯一性的增強型Servlet容器。 明確了這一點,我們就可以想象的到,DispatcherServlet具有servlet的大多數特性,包括Servlet的完整生命週期。

Spring中通過DispatcherServlet來處理所用的請求!爲什麼這樣設計呢?具體可以參考前端設計模式

​ 分析初始化之前先了解一下DispatcherServlet的繼承關係:

在這裏插入圖片描述
​ 首先瞭解到Servlet的初始化時會調用其inti(),DispatcherServlet也不例外。發現DispatcherServlet中沒有該方法,繼續向上看。FrameworkServlet中也沒有,繼續向上;發現HttpServletBean存在。

public final void init() throws ServletException {
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                //這裏就是加載servlet的環境吧
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// Let subclasses do whatever initialization they like.
    //重點關注這個方法
		initServletBean();

	}

​ 發現其中調用了initServletBean(),發現在HttpServletBean沒有具體實現,看他的子類FrameworkServlet,其中得到了webApplicationContext對象,在之前,ContextLoaderListener加載的時候已經創建了WebApplicationContext實例(具體可以查看ContextLoaderListener源碼分析),而在這裏是對這個實例的進一步補充初始化。這個方法在HttpServletBean的子類FrameworkServlet中得到了重寫。

注:ContextLoaderListener載入的是除DispatcherServlet之外的其他的上下文配置文件,而Spring MVC的配置文件是在DispatcherServlet中設置的。

protected final void initServletBean() throws ServletException {
		try {
            //initWebApplicationContext()來得到webApplicationContext對象
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
		catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
	}

initFrameworkServlet()是給子類來覆蓋的方法,但是我在DispatcherServlet中並沒有找到,查看initWebApplicationContext(),還是在FrameWorkServlet中。

protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

initWebApplicationContext()方法主要用於創建或刷新WebApplicationContext實例,並對Servlet功能所使用的一些環境變量進行初始化。首先通過findWebApplicationContext()中調用getContextAttribute()來查找ServletContext進行初始化webApplicationContext實例。如果沒有,就通過ContextLoaderListener獲得的rootContext重新創建一個實例createWebApplicationContext(rootContext)。

protected WebApplicationContext findWebApplicationContext() {
		String attrName = getContextAttribute();
		if (attrName == null) {
			return null;
		}
		WebApplicationContext wac =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
		if (wac == null) {
			throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
		}
		return wac;
	}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		Class<?> contextClass = getContextClass();
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Servlet with name '" + getServletName() +
					"' will try to create custom WebApplicationContext context of class '" +
					contextClass.getName() + "'" + ", using parent context [" + parent + "]");
		}
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException(
					"Fatal initialization error in servlet with name '" + getServletName() +
					"': custom WebApplicationContext class [" + contextClass.getName() +
					"] is not of type ConfigurableWebApplicationContext");
		}
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//這裏設置實例的一些參數屬性
		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		wac.setConfigLocation(getContextConfigLocation());

		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

​ 這些操作都是對webApplicationContext實例的進一步初始化!那麼Servlet中配置文件的加載和與rootContext整合是如何進行的?(webApplicationContext是所有Servlet的父環境!),答案是在initWebApplicationContext()中的configureAndRefreshWebApplicationContext()調用的refresh();

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
    //這裏進行整合,具體的源碼就不看了
		wac.refresh();
	}

​ 那麼DisapatcherServlet的初始化是如何進行的?SpringMVC中的組件是如何加載的?答案在initWebApplicationContext()中的onRefresh()方法中,該方法在其子類DispatcherServlet的onRefresh()方法中進行了重寫。而在onRefresh()方法中調用了initStrategies()方法來完成初始化工作,初始化Spring MVC的9個組件。

protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

initStrategies()完成了對組件的初始化

protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

到這就完成了init()的初始化!真累啊。

2.執行過程

當一個get/post請求進來後,會執行sevlet的doPost/doGet方法。以post請求爲例。在DispatcherServlet中沒有找到,找其父類FrameWorkServlet,發現!

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

		processRequest(request, response);
	}

調用了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 = 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 ex) {
			failureCause = ex;
			throw ex;
		}
		catch (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();
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				}
				else {
					if (asyncManager.isConcurrentHandlingStarted()) {
						logger.debug("Leaving response open for concurrent processing");
					}
					else {
						this.logger.debug("Successfully completed request");
					}
				}
			}

			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

查看doService()具體實現在DispatcherServlet

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(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<String, Object>();
			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());

		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);
				}
			}
		}
	}

其中調用了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對象
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
                //通過request請求初始化HandlerExecutionChain對象
				mappedHandler = getHandler(processedRequest);//重點關注這個方法
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
                //根據handler選擇合適的HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()/*這個方法返回的是一個Object handler對象*/);

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						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;
				}

				// Actually invoke the handler.
                //適配器處理handler後返回ModelAndView對象
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//視圖對象的處理
				applyDefaultViewName(processedRequest, mv);
			//applyPostHandle()該方法會獲得攔截器對象數組。HandlerInterceptor[] interceptors = getInterceptors();
                
                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);
			}
            //處理最終結果 渲染視圖等 重點看這裏
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
            ......
	}

​ doDispatch()做了很多事情,最主要的就是爲handler選擇相應的處理器適配器(HandlerAdapter)並且處理handler返回ModelAndView對象。最後會渲染視圖。

先查看其中的mappedHandler = getHandler()做了那些事情:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		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;
	}

​ 創建了一個處理器映射器(HandlerMapping)對象hm,並且得到了一個處理器執行鏈(HandlerExecutionChain)對象,(該對象包含了攔截器對象(HandlerInterceptor)和handler對象)。在doDispatch()和下面的getHandler()會體現。

​ 查看其中的hm.getHandler()具體做了那些事!該方法是HandlerMapping接口中的方法。具體實現類AbstractHandlerMapping實現了該方法。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //根據請求獲取相應的handler,重點看這裏
		Object handler = getHandlerInternal(request);
		if (handler == null) {
            //沒有查找到就是用默認的Handler
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
    //如果handler是字符串類型 說明是bean名稱 需要獲取handler bean對象
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}
//這裏封裝執行鏈對象,重點看這裏
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

​ 首先具體查看getHandlerInternal()是如何通過request獲得handler對象。該方法在AbstractHandlerMapping的子類AbstractHandlerMethodMapping中有實現:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //獲取request中的url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
            //根據request中的url來獲取HandlerMethod對象(實際就是handler,因爲最後返回的就是這個對象)
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

查看lookupHandlerMethod()的具體實現;

// AbstractHandlerMethodMapping
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    //創建匹配集合
   List<Match> matches = new ArrayList<>();
   // 直接匹配
   List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
   // 存在匹配 則添加到匹配集合中
   if (directPathMatches != null) {
      addMatchingMappings(directPathMatches, matches, request);
   }
   // 沒有匹配 遍歷所有處理方法查找
   if (matches.isEmpty()) {
      // No choice but to go through all mappings...
      addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
   }

   // 存在匹配
   if (!matches.isEmpty()) {
      Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
      matches.sort(comparator);
      if (logger.isTraceEnabled()) {
         logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
      }
      // 排序之後獲取第一個
      Match bestMatch = matches.get(0);
      // 有多個匹配 會找出第二個進行比較
      if (matches.size() > 1) {
         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();
            throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                  request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
         }
      }
      // 設置request參數
      handleMatch(bestMatch.mapping, lookupPath, request);
      // 返回匹配的url處理方法
      return bestMatch.handlerMethod;
   }
   else {
      // 處理沒有匹配情況直接返回null
      return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
   }
}

​ 這樣成功匹配到handler對象,接下來在看getHandlerExecutionChain()是如何處理HandlerExecutionChain對象。AbstractHandlerMapping類中。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    // 如果當前handler不是執行鏈類型 則創建一個新的執行鏈封裝
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
	//當前url
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    //遍歷攔截器 與當前url匹配的添加至執行鏈中
		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;
	}

​ 到這裏我們完成了HandlerExecutionHandler對象和其中的Handler對象以及HandlerInterrceptor攔截器對象的初始化,以及獲得相應的HandlerAdapter處理器適配器來處理handler對象,然後返回ModelAndView對象上面的都是在DispatcherServlet這個類的doDispatch()方法中進行的!

​ 現在我們還差渲染視圖這個工作,也是在doDispatch()中調用的processDispatchResult()該方法進行的。

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) {
         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);
      }
   }

   // Did the handler return a view to render?
   if (mv != null && !mv.wasCleared()) {
      // 渲染
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      if (logger.isDebugEnabled()) {
         logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
               "': assuming HandlerAdapter completed request handling");
      }
   }

   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }

   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

render()方法。

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
   // Determine locale for request and apply it to the response.
   // 設置本地化
   Locale locale =
         (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
   response.setLocale(locale);

   View view;
   String viewName = mv.getViewName();
   if (viewName != null) {
      // We need to resolve the view name.
      // 解析視圖名獲取視圖對象
      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 {
      // No need to lookup: the ModelAndView object contains the actual View object.
      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() + "'");
      }
   }

   // Delegate to the View object for rendering.
   if (logger.isDebugEnabled()) {
      logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
   }
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }
      // 委託給視圖進行渲染
      view.render(mv.getModelInternal(), request, response);
   }
   catch (Exception ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
               getServletName() + "'", ex);
      }
      throw ex;
   }
}

到這DispatcherServlet的執行流程基本就結束了。

最後附上基本流程圖:

在這裏插入圖片描述

參考:https://blog.csdn.net/qq_38410730/article/details/79426673

https://blog.csdn.net/laravelshao/article/details/82763436

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章