Spring MVC
中,一般来讲,控制器指的是开发人员使用了注解@Controller
这样的类,而控制器方法,是控制器类中使用了注解@RequestMapping
的那些方法。控制器方法能处理哪些请求,就是通过这些@RequestMapping
注解信息来定义的,DispatcherServlet
会把这些信息加载保存到一个RequestMappingHandlerMapping
对象中,当相应的请求到达时,DispatcherServlet
会通过该信息找到相应的控制器方法,然后通过调用RequestMappingHandlerAdapter
执行找到的控制器方法,完成对请求的处理。
RequestMappingHandlerAdapter
执行控制器方法的过程,听起来很简单,似乎就是一个一般方法调用的问题,其实不然,这里面牵涉到相当多的步骤和逻辑,如果不了解这些步骤和逻辑,遇到问题时我们通常会无从下手。而如果对此过程透彻了解,当问题再出现的时候,我们就能够轻松推断出问题所在从而迅速解决,甚至能提前准备预防问题发生。
这个系列的文章,就打算结合源代码,系统性地分析一下通过RequestMappingHandlerAdapter
执行控制器方法处理请求的整个过程。这个过程大致分为如下几个步骤,每个步骤将对应一篇文章 :
- 执行信息记录对象
ModelAndViewContainer
的准备 - 请求参数的获取
- 控制器方法参数值绑定
HandlerMethodArgumentResolver
- 控制器方法参数值的验证
MethodValidationInterceptor
- 调用控制器方法本身
- 控制器方法返回值处理
- 包装返回结果 : 从
ModelAndViewContainer
对象构造ModelAndView
对象
读完该系列文章,相信你对控制器方法处理请求的整个过程,会有足够深入透彻的理解。
注意,
HandlerInterceptor
的应用不在本系列文章的关注范围之内,如果想了解HandlerInterceptor
的应用,可以参考 :DispatcherServlet 请求处理主逻辑 : 3. 通过 HandlerAdapter 执行 Handler
在分篇讲解之前,我们先看一下RequestMappingHandlerAdapter
调用控制器方法的入口 :
// RequestMappingHandlerAdapter 代码片段
// 返回值是一个 ModelAndView
// 如果需要调用者后续解析和渲染视图,则该返回值 ModelAndView 不会为 null
// 如果请求已经被该方法完全处理,不需要调用者后续解析和渲染视图,则返回值 ModelAndView 会是 null
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 将 HttpServletRequest/HttpServletResponse 对象包装成一个 ServletWebRequest 对象
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 准备工作,设置调用控制器方法所需要的各种数据,或者工具组件
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 将目标控制器方法 handlerMethod 包装成一个 ServletInvocableHandlerMethod,
// 这里主要是针对 Servlet 环境的一些特殊处理和包装,参数传递
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 是一个调用目标控制器方法过程中,记录参数解析,返回值处理的一些
// 决策信息的一个容器类型/持有器类型,这里准备出这样一个对象,供控制器方法调用过程中使用
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 将请求中可能存在的 FlashMap 属性添加到 mavContainer
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 1. 获取所有 @SessionAttributes 属性到 mavContainer
// 2. 执行所有 @ModelAttribute 方法
// 3. 找到所有使用了注解 @ModelAttribute 并且也属于 @SessionAttributes 列表的
// 参数将其添加到 mavContainer
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 设置重定向时如果 RedirectAttributes 属性未设置,是否要使用缺省 Model,
// 缺省值为 false, 表示即使重定向时 RedirectAttributes 属性未设置,也不要使用缺省 Model
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 对异步Web请求的处理的准备工作,本系列文章中只介绍同步请求处理,所以这里忽略对这一部分的介绍
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;
}
// 目标控制器方法执行完结,请求处理已经完成,执行过程中的各种决定信息都记录在了
// mavContainer 中,现在结合相关组件构造一个 ModelAndView 对象,后续调用者
// 有可能还有基于这些信息解析和渲染视图
// 如果控制器方法已经完全处理了该请求,无需后续处理,则 mavContainer 会包含
// 这样的信息 : #isRequestHandled == true, 从而这里的 ModelAndView 返回值
// 会是 null
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
本系列文章链接合集 :