文章目录

0.前记

根据上一篇文章可知在​​DispatcherServlet#doDispatcher()​​中进行处理目标方法的执行的时候, 会有参数解析器和返回值解析器进行解析执行。

而根据之前的文章可知, 参数解析器和返回值解析器都是在​​DispatcherServlet​​初始化的时候会初始化, 加入一定数量的返回值解析器。

RequestMappingHandlerAdapter.invokeHanddlerMethod()

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (十)_初始化


而因为​​RequestMappingHandlerAdapter​​​实现了​​InitiliazingBean​​​ 接口, 所以会实现 ​​afterPropertiesSet​​​方法, 而这个方法会初始化 上图的 ​​this.argumentResolvers​​​ 和 ​​this.returnValueHandlers​

所以本章根据之前的参数解析器和返回值解析器, 来说明一些他们的一些执行流程。

1.返回值解析器的流程

上一篇说明了参数解析器的流程, 本文接着说明返回值解析器的流程

ServletInvocableHandlerMethod.invokeAndHandle

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (十)_java_02


​HandlerMethodReturnValueHandlerComposite.handleReturnValue()​

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (十)_开发语言_03

  1. 先检查这个返回值解析器是否符合要求​​selectHandler​
  2. 执行返回值解析器的​​handleReturnValue​​方法

​HandlerMethodReturnValueHandlerComposite.selectHandler()​

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
  1. 先判断是否是异步的处理
  2. 然后利用​​supportsReturnType​​ 方法判断是否符合要求

比如说 ​​RequestResponseBodyMethodProcessor​​​ 的 ​​supportsReturnType​​ 方法

@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}

通过判断是否有 ​​@RequestBody​​​ 注解和 方法上有无 ​​@RequestBody​​ 注解来判断是否符合要求

​RequestResponseBodyMethodProcessor.handleReturnValue()​

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

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

// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

通过​​MessageConverters​​组件进行了基本数据和json之间的数据转换

​AbstractMessageConverterMethodProcessor.writeWithMessageConverters()​

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...

2.返回值封装ModelAndView

执行了目标方法后, 需要返回值封装为ModelAndView

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (十)_数据_04


​RequestMappingHandlerAdapter.getModelAndView()​

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
  1. session 中的数据转到 request域中
  2. 创建 ModelAndView
  3. 判断是否为视图引用, 判断是否为重定向视图

这里需要注意的是如果是重定向视图的话, 需要先将数据移到request域, 再将数据移到session域, 因为只有session域中重定向可以拿到之前的数据。

最后在​​DispatcherServlet.doDispatcher()​​得到了ModelAndview的数据

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (十)_数据_05

3.applyDefaultViewName方法

这个方法是为了如果没有视图的话会默认给一个视图

SpringMvc 源码分析 (RequestMappingHandlerAdapter 的使用细节) (十)_java_06

private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}

protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
}

​DefaultRequestToViewNameTranslator.getViewName()​

public String getViewName(HttpServletRequest request) {
String path = ServletRequestPathUtils.getCachedPathValue(request);
return (this.prefix + transformPath(path) + this.suffix);
}

​ServletRequestPathUtils.getCachedPathValue()​

public static String getCachedPathValue(ServletRequest request) {
Object path = getCachedPath(request);
if (path instanceof PathContainer) {
String value = ((PathContainer) path).value();
path = UrlPathHelper.defaultInstance.removeSemicolonContent(value);
}
return (String) path;
}


public static Object getCachedPath(ServletRequest request) {

// The RequestPath is pre-parsed if any HandlerMapping uses PathPatterns.
// The lookupPath is re-resolved or cleared per HandlerMapping.
// So check for lookupPath first.

String lookupPath = (String) request.getAttribute(UrlPathHelper.PATH_ATTRIBUTE);
if (lookupPath != null) {
return lookupPath;
}
RequestPath requestPath = (RequestPath) request.getAttribute(PATH_ATTRIBUTE);
if (requestPath != null) {
return requestPath.pathWithinApplication();
}
throw new IllegalArgumentException(
"Neither a pre-parsed RequestPath nor a pre-resolved String lookupPath is available.");
}

这里会将request域中的请求路径直接当作页面地址。比如说返回值为void的话, 会直接去@RequestMapping() 的路径