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中
返回值解析器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
处理controller层方法的返回值,默认有15个,可自定义,mvcConfig中
准备ModelAndView
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
ModelFactory是用来维护Model的,具体包含两个功能
- 初始化Model
- 处理器执行后将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
doInvoke
调用我们的controller层的方法,如果我们引入了SpringValidation框架,Controller类会被代理,拦截到参数做校验。
返回值处理
前面提到的15个返回值处理器,依次尝试每个返回值处理器是否支持处理,找到后开始处理
@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写入响应消息
- HttpMessageConveter,可自定义,mvcConfig中
- 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);
总结
- 返回值解析器、参数解析器可在MvcConfig中配置
- 如果引入了校验框架,controller会被代理,参数解析完成后进行校验
- 返回MediaType类型可在MvcConfig中配置ContentNegotiation
- 在写入返回消息、获取请求体之前,可自定义钩子,最便捷的方法是通过@ControllerAdvice
- 将返回Java类型转换成响应体有HttpMessageConveter实现,可在MvcConfig中配置
参考文章
Q.E.D.