SpringMVC执行流程,源码分析(二)

真正处理请求

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

这里会调用匹配到的HandlerAdapter中的handle方法,真正处理请求,包含参数校验、参数解析、反射调用处理方法、处理返回值(View或者ResponseBody)、全局异常处理这些熟悉的流程。HandlerAdapter就前面那么几个,这里以最常用的RequestMappingHandler为例

准备工作

准备

/* 数据绑定 */
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
/* Model处理 */
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
/* 包装HandlerMethod */
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
...
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

参数解析器

if (this.argumentResolvers != null) {
	invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}

默认有27个,可自定义,mvcConfig中
argResolvers.png

返回值解析器

if (this.returnValueHandlers != null) {
	invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}

处理controller层方法的返回值,默认有15个,可自定义,mvcConfig中
retValueResolvers.png

准备ModelAndView

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

ModelFactory是用来维护Model的,具体包含两个功能

  1. 初始化Model
  2. 处理器执行后将Model中相应的参数更新到SessionAttributes中

更多内容,可以查看ModelFactory源码分析

准备异步

AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
/* 在doDispatch方法中,已经为request设置了asyncManager */
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
/* 其实还是用线程池实现的异步 */
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
/* 异步执行的拦截器 */
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
/* 异步已有结果,说明该请求是执行完controller后再转发到dispatchServlet中,请求处理完成 */
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);
}

更多异步内容可以查看SpringMVC异步

通过反射执行目标方法

invocableMethod.invokeAndHandle(webRequest, mavContainer);

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	return doInvoke(args);
}

参数解析

前面的27个argurementResolvers
参数解析.png

doInvoke

调用我们的controller层的方法,如果我们引入了SpringValidation框架,Controller类会被代理,拦截到参数做校验。

返回值处理

前面提到的15个返回值处理器,依次尝试每个返回值处理器是否支持处理,找到后开始处理
返回值解析.png

@ResponseBody返回值为例继续分析,该处理器为RequestResponseBodyMethodProcessor,在其writeWithMessageConverters中,处理逻辑比较长。

选择返回值处理器

HandlerMethodReturnValueHandler是一个接口,实现该接口并在MVCConfig中配置我们自己也可以定义返回值处理器,其中的supportsReturnType方法决定是否选择该handler。

HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
	throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}

准备serverResponse,outputMessage

这两个message是ServletServerHttpRequest、ServletServerHttpResponse,是对HttpServetRequest,Response做的包装

mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

解析返回值类型

Object body;
Class<?> valueType;
Type targetType;

if (value instanceof CharSequence) {
	body = value.toString();
	valueType = String.class;
	targetType = String.class;
}
else {
	body = value;
	valueType = getReturnValueType(body, returnType);
	targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
/* Resource */
if (isResourceType(value, returnType)) {...}
}

解析MediaType

其中的getAcceptableMediaTypes方法依赖于contentNegotiationManager,可在mvcConfig中配置

HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes;
try {
	acceptableTypes = getAcceptableMediaTypes(request);
}
catch (HttpMediaTypeNotAcceptableException ex) {
	int series = outputMessage.getServletResponse().getStatus() / 100;
	if (body == null || series == 4 || series == 5) {
		if (logger.isDebugEnabled()) {
			logger.debug("Ignoring error response content (if any). " + ex);
		}
		return;
	}
	throw ex;
}
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
...
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
	for (MediaType producibleType : producibleTypes) {
		if (requestedType.isCompatibleWith(producibleType)) {
			mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
		}
	}
}

HttpMessageConveter写入响应消息

  1. HttpMessageConveter,可自定义,mvcConfig中
  2. ResponseBodyAdvice钩子,可自定义,通过@ControllerAdvice,或自定义RequestHandlerMappingAdpter或ExceptionHandlerExceptionResolver,自定义方法参见该系列文章一
    RequestBodyAdvice(异常处理器中不能配置)同理
/* 选择HttpMessageConverter,可在WebMVCconfig中配置 */
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
		(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
		((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
		converter.canWrite(valueType, selectedMediaType)) {
	/* ResponseBodyAdvice对象钩子 */
	body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
			(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
			inputMessage, outputMessage);
	if (body != null) {
		Object theBody = body;
		LogFormatUtils.traceDebug(logger, traceOn ->
				"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
		addContentDispositionHeader(inputMessage, outputMessage);
		/* 写入消息体 */
		if (genericConverter != null) {
			genericConverter.write(body, targetType, selectedMediaType, outputMessage);
		}
		else {
			((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
		}
	}
	...
}

返回视图?

如果返回的是视图,那么mav会被处理
如果返回的不是视图,那么request已被处理,本次请求中,View是无效的,getModelAndView返回null

invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
	return null;
}

return getModelAndView(mavContainer, modelFactory, webRequest);

总结

  1. 返回值解析器、参数解析器可在MvcConfig中配置
  2. 如果引入了校验框架,controller会被代理,参数解析完成后进行校验
  3. 返回MediaType类型可在MvcConfig中配置ContentNegotiation
  4. 在写入返回消息、获取请求体之前,可自定义钩子,最便捷的方法是通过@ControllerAdvice
  5. 将返回Java类型转换成响应体有HttpMessageConveter实现,可在MvcConfig中配置

参考文章

ModelFactory源码分析

Q.E.D.


一切很好,不缺烦恼。