文章目录
0.前记
根据上一篇文章可知在DispatcherServlet#doDispatcher()
中进行处理目标方法的执行的时候, 会有参数解析器和返回值解析器进行解析执行。
而根据之前的文章可知, 参数解析器和返回值解析器都是在DispatcherServlet
初始化的时候会初始化, 加入一定数量的返回值解析器。
RequestMappingHandlerAdapter.invokeHanddlerMethod()
而因为RequestMappingHandlerAdapter
实现了InitiliazingBean
接口, 所以会实现 afterPropertiesSet
方法, 而这个方法会初始化 上图的 this.argumentResolvers
和 this.returnValueHandlers
所以本章根据之前的参数解析器和返回值解析器, 来说明一些他们的一些执行流程。
1.返回值解析器的流程
上一篇说明了参数解析器的流程, 本文接着说明返回值解析器的流程
ServletInvocableHandlerMethod.invokeAndHandle
HandlerMethodReturnValueHandlerComposite.handleReturnValue()
- 先检查这个返回值解析器是否符合要求
selectHandler
- 执行返回值解析器的
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;
}
- 先判断是否是异步的处理
- 然后利用
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
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;
}
- session 中的数据转到 request域中
- 创建 ModelAndView
- 判断是否为视图引用, 判断是否为重定向视图
这里需要注意的是如果是重定向视图的话, 需要先将数据移到request域, 再将数据移到session域, 因为只有session域中重定向可以拿到之前的数据。
最后在DispatcherServlet.doDispatcher()
得到了ModelAndview的数据
3.applyDefaultViewName方法
这个方法是为了如果没有视图的话会默认给一个视图
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() 的路径