MVC源碼解析之doDispatch執行流程

       之前也已經總述過:AOP執行原理、SpringMvc執行流程,但總覺的有些點說的含糊不清,後續就把含糊不清的點,細緻的講解一下。現在先說請求核心處理doDispatch()方法。

上圖是默認容器啓動後IOC裝載的:HandlerAdapters和HandlerMapping(有印象,後續用)。

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 {
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);
			
			//1. 獲取handler執行器鏈HandlerExecutionChain.
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}
			
			//2. 獲取目標handler適配器HandlerAdapter執行目標Handler
			HandlerAdapter ha = 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}
			
			//3. 執行目標Handler,返回ModelAndView.
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			applyDefaultViewName(processedRequest, mv);
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		catch (Throwable err) {
			dispatchException = new NestedServletException("Handler dispatch failed", err);
		}
		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);
			}
		}
	}
}

圖中標的1-3,就是我們常說的主要執行流程。根據mvc執行圖,腦海中會有簡單清晰的執行流程。但現在主要闡述這三步具體的執行流程:

1. mappingHandler = getHandler(processedRequest); 

DispatcherServlet.java中:  遍歷已經裝載的默認的HandlerMaping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);    <-----
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

AbstractHandlerMapping.java中:  查找目標Handler,並添加攔截器。(給HandlerExecuterChain中 [private List<HandlerInterceptor> interceptorList;] 屬性賦值)
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	Object handler = getHandlerInternal(request);                           <-----
	if (handler == null) {
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}
	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}

AbstractHandlerMethodMapping.java中:  根據uri查找目標Handler方法。(給HandlerExecuterChain中 [private final Object handler;] 屬性賦值)
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	this.mappingRegistry.acquireReadLock();
	try {
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

2. HandlerAdapter ha = getHandlerAdapter(mappingHandler.getHandler());

DispatcherServlet.java中: 遍歷裝載的HandlerAdapter(開頭debug容器裝載圖),獲取支持當前handler的HandlerAdapter.
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
		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");
}

3. mv = ha.handler(processedRequest,response,mappingHandler.getHandler());

1. AbstactHandlerMappingAdapter.java中: (HandlerAdapter類圖: xxxHandlerAdapter extends AbstactHandlerMappingAdapter implements HandlerAdapter)
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	return handleInternal(request, response, (HandlerMethod) handler);                   <---------
}

2. RequestMappingHandlerAdapter.java中:
protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	ModelAndView mav;
	checkRequest(request);
	//檢測是否需要加鎖執行synchronize
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			//Spring自帶的WebUtils工具類
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}else {
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}else {
		mav = invokeHandlerMethod(request, response, handlerMethod);                     <----------
	}
	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}
	return mav;
}

3. RequestMappingHandlerAdapter.java中:(doDispatch()上面挑選的HandlerMappingAdapter) 對於mv這步,暫時不講述
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
	HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
		//主要看這個類的動作
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			LogFormatUtils.traceDebug(logger, traceOn -> {
				String formatted = LogFormatUtils.formatValue(result, !traceOn);
				return "Resume with async result [" + formatted + "]";
			});
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}

		invocableMethod.invokeAndHandle(webRequest, mavContainer);                        <------------
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

4. ServletInvocableHandlerMethod.java : 調用該方法並通過已配置的方法之一處理返回值(ServletInvocableHandlerMethod extends InvocableHandlerMethod)
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
    //執行方法,返回結果,或許再根據已註冊的HandlerMethodReturnValueHandler解析
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);		   <------------
	setResponseStatus(webRequest);
	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}
	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}

5. InvocableHandlerMethod.java : 
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	//參數解析,將url中的參數值解析
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	//利用JavaReflect完成方法調用
	return doInvoke(args);
}

6. InvocableHandlerMethod.java : 
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	MethodParameter[] parameters = getMethodParameters();
	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		args[i] = resolveProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		//檢測、使用參數解析器HandlerMethodArgumentResolver解析參數
		if (this.argumentResolvers.supportsParameter(parameter)) {
			try {
				args[i] = this.argumentResolvers.resolveArgument(
						parameter, mavContainer, request, this.dataBinderFactory);
				continue;
			}catch (Exception ex) {
				// Leave stack trace for later, e.g. AbstractHandlerExceptionResolver
				if (logger.isDebugEnabled()) {
					String message = ex.getMessage();
					if (message != null && !message.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, message));
					}
				}
				throw ex;
			}
		}
		if (args[i] == null) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
	}
	return args;
}

整個流程貌似可以說的通,再屢一下,很容易發現,沒有對已經裝載的參數解析器,進行遍歷的代碼。
真的沒有嗎?
再去看一下最後InvocableHandlerMethod.getMethodArgumentValues()方法,其中有段代碼有檢測的
是否支持已裝載的參數解析器:if(this.argumentResolvers.supportsParameter(parameter))。順着
這條再去看一下InvocableHandlerMethod類中argumentResolvers屬性類型,會發現是使用混合模型的
對象:HandlerMethodArgumentResolverComposite argumentResolvers。這樣很容易發現,遍歷參數解
析器的操作是在HandlerMethodArgumentResolverCompoiste中執行。
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
	//這個argumentResolverCache,是優化請求處理,使用的緩存:Map<MethodParameter, HandlerMethodArgumentResolver>
	HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
	if (result == null) {
		for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
			if (methodArgumentResolver.supportsParameter(parameter)) {
				result = methodArgumentResolver;
				this.argumentResolverCache.put(parameter, result);
				break;
			}
		}
	}
	return result;
}

HandlerAdapter:(HandlerAdapter子類需要實現HandlerAdapter接口,並實現supports(Object handler)方法,而supports()方法返回boolean類型,決定了當前HandlerAdapter是否支持使用。)

RequestMappingHandlerAdapter: handler方法使用@RequestMapping、@Controller等註解

SimpleControllerHandlerAdapter: handler類實現Controller接口

HttpRequestHandlerAdapter:handler類實現HttpRequetHandler接口

HandlerMapping:(上面已經截圖)

HandlerMethodArgumentResolver:RequestMappingHandlerAdapter.getDefaultArgumentResolvers()。

參考文章: https://www.cnblogs.com/w-y-c-m/p/8443892.html

//默認的參數解析,創建了默認的24個參數解析器,並添加至resolvers
//這裏的24個參數解析器都是針對不同的參數類型來解析的
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
	List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

	// 基於註解的參數解析器

	//一般用於帶有@RequestParam註解的簡單參數綁定,簡單參數比如byte、int、long、double、String以及對應的包裝類型
	resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
	//用於處理帶有@RequestParam註解,且參數類型爲Map的解析綁定
	resolvers.add(new RequestParamMapMethodArgumentResolver());
	//一般用於處理帶有@PathVariable註解的默認參數綁定
	resolvers.add(new PathVariableMethodArgumentResolver());
	//也是用於帶有@PathVariable註解的Map相關參數綁定,後續還有一些默認的參數解析器。後續還有一些參數解析器,我這裏都不一一解釋了。想具體確認某個參數會交個哪個參數解析器處理,可以通過以下解析器的supportsParameter(MethodParameter parameter)方法得知
	resolvers.add(new PathVariableMapMethodArgumentResolver());
	resolvers.add(new MatrixVariableMethodArgumentResolver());
	resolvers.add(new MatrixVariableMapMethodArgumentResolver());
	resolvers.add(new ServletModelAttributeMethodProcessor(false));
	resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
	resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
	resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new RequestHeaderMapMethodArgumentResolver());
	resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
	resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));

	// 基於類型的參數解析器
	resolvers.add(new ServletRequestMethodArgumentResolver());
	resolvers.add(new ServletResponseMethodArgumentResolver());
	resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
	resolvers.add(new RedirectAttributesMethodArgumentResolver());
	resolvers.add(new ModelMethodProcessor());
	resolvers.add(new MapMethodProcessor());
	resolvers.add(new ErrorsMethodArgumentResolver());
	resolvers.add(new SessionStatusMethodArgumentResolver());
	resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

	// Custom arguments
	if (getCustomArgumentResolvers() != null) {
		resolvers.addAll(getCustomArgumentResolvers());
	}
	// Catch-all
	resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
	resolvers.add(new ServletModelAttributeMethodProcessor(true));

	return resolvers;
}

常見使用場景:SpringBoot中implement WebMvcConfigurer具體實現相關配置

1. 攔截器Interceptor

2. 參數解析器HandlerMethodArgumentResolver

&3.消息轉換器MessageConverters

流程體會:

1. 父類接口、抽象類、實現類: 模板設計模型

2. 混合設計模式: 參考 HandlerMethodArgumentResolverComposite (實現父類方法,並設置父類集合作爲屬性)

3. Spring中自帶的一些工具類的使用

 

發佈了32 篇原創文章 · 獲贊 0 · 訪問量 4056
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章