一、回顾
在 《Spring-MVC》 中讲解了 Spring MVC 的工作机制。
第一步、 请求 Request 进入到 Spring 应用中,统一由分发器 DispatcherServlet 进行接收处理。
第二步、 DispatcherServlet 根据请求路径,从 HandlerMapping 中获取对应的 controller ,并将结果返回为 DispatcherServlet。
第三步、 DispatcherServlet 根据 RequestMapping 返回的 Handler 去 HandlerAdapter 中获取对应适配的结果返回给 DispatcherServlet,如:ModelAndView
第四步、 DispatcherServlet 根据返回的 ModelAndView 到 ViewResolver 中进行解析,最后得到响应 View
二、Spring MVC 简单案例
@Controllerpublic class SpringMvcController {@RequestMapping("/sys/request-body")@ResponseBodypublic SysDict requestBody(@RequestBody SysDict sysDict){return sysDict;}@RequestMapping("/sys/obj")@ResponseBodypublic SysDict obj(SysDict sysDict){return sysDict;}@RequestMapping("/sys/request-param")@ResponseBodypublic String requestParam(@RequestParam String sysDict){return sysDict;}@RequestMapping("/sys/date")@ResponseBodypublic Date testDate(@DateTimeFormat(pattern = "yyyy-HH-mm HH:mm:ss") Date date){return date;}}
以上的几个接口中,各自方法接收了不同的参数类型,并通过譬如:@RequestParam 、@DateTimeFormat 等方式对参数进行解析赋值。
问题
Spring MVC 在请求参数与方法绑定前如何对参数进行解析?且在返回结果后对结果集进行解析?
通过 Spring MVC 的工作流程,能够直接的猜测Spring MVC 参数解析赋值的操作在 第三步 HandlerAdapter 的执行过程中。
三、源码解析
Spring MVC 在请求参数与方法绑定前如何对参数进行解析?且在返回结果后对结果集进行解析?
Spring MVC 构造 ServletInvocableHandlerMethod
在 Spring MVC 中 RequestMappingHandlerAdapter 是全局的请求适配器,针对每一次请求,Spring MVC 会构造 ServletInvocableHandlerMethod 进行请求调用。 而 请求参数解析器集合 和 响应结果解析器集合 在构造 ServletInvocableHandlerMethod 时会作为属性设置到 ServletInvocableHandlerMethod 中。
聚焦 DispatcherServlet#doDispatch 代码
public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// Determine handler adapter for the current request.// 获取 HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Actually invoke the handler.// 执行 HandlerAdapter#handlemv = ha.handle(processedRequest, response, mappedHandler.getHandler());}}
Spring MVC 中 DispatcherServlet 进行请求处理的主要两个步骤:
- 步骤一:获取 HandlerAdapter (默认实现:RequestMappingHandlerAdapter)
- 步骤二:执行 HandlerAdapter#handle
下面我们来看看 默认实现:RequestMappingHandlerAdapter#handler 方法的实现
RequestMappingHandlerAdapter#handler

主要的执行逻辑方法为 RequestMappingHandlerAdapter#invokeHandlerMethod
聚焦 RequestMappingHandlerAdapter#invokeHandlerMethod
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 请求数据解析类集合if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// 响应数据解析类集合if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}}}
在该方法中对请求执行对象 ServletInvocableHandlerMethod 进行属性赋值,其中主要的两个属性对象:
请求参数解析器集合
HandlerMethodArgumentResolverComposite argumentResolvers
响应结果解析器集合
HandlerMethodReturnValueHandlerComposite returnValueHandlers
3.1、请求参数解析分析
分析解析器集合
分析请求参数解析器集合
HandlerMethodArgumentResolverComposite
HandlerMethodArgumentResolverComposite 类图如下:
先来看看接口定义 HandlerMethodArgumentResolver
// @since 3.1public interface HandlerMethodArgumentResolver {// 判断方法参数,是否符合解析条件boolean supportsParameter(MethodParameter parameter);// 进行解析Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory);}
方法参数解析 接口定义了两个方法:
- HandlerMethodArgumentResolver#supportsParameter
判定方法参数是否满足此解析器的条件 - HandlerMethodArgumentResolver#resolveArgument
进行参数解析
在这里,大体可以猜得出来,这个请求入参解析器,会针对每一个请求入参进行 supportsParameter 判断,符合条件的就进行 resolveArgument 解析操作。
再来看看 HandlerMethodArgumentResolverComposite 具体实现代码,如下:
// 请求入参解析器集合public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {// 维护的 入参解析器private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();// 维护的缓存,用于提高性能private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =new ConcurrentHashMap<>(256);// 针对 List<HandlerMethodArgumentResolver> 的增删改查操作public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) { ...... }public List<HandlerMethodArgumentResolver> getResolvers() { ...... }public void clear() { ...... }public Object resolveArgument( ......){ ...... }private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { ...... }// 执行解析@Overridepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 获取解析并执行解析操作HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +" supportsParameter should be called first.");}return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}// 获取匹配的解析执行器private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {// 从缓存中取HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {// 缓存没有,遍历 解析器集合,分别调用 supportsParameter 获取for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {if (methodArgumentResolver.supportsParameter(parameter)) {result = methodArgumentResolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;}}
HandlerMethodArgumentResolverComposite 从接口定义来说也是一个 请求入参解析器,不过它是一个解析器集合,相当于一个代理人,或者委托人,主要功能信息如下:
1、维护了一个 List 集合 argumentResolvers 用来存放 HandlerMethodArgumentResolver 解析器。
2、提供增删改的操作进行 List 集合的维护。
3、业务操作;
- a、supportsParameter 判断操作,委派给 List 中的解析器完成
- b、resolveArgument 解析操作,也委派给 List 中的解析器完成
解析器如何生效
再次回到 HttpRequestHandlerAdapter#invokeHandlerMethod 方法。
该方法构建 ServletInvocableHandlerMethod 对象并设置相关参数,包括 请求参数解析器集合 HandlerMethodArgumentResolverComposite ,在环境和参数准备完成之后,调用 ServletInvocableHandlerMethod#invokeAndHandle 进行方法请求调用。
聚焦 ⑦ InvocableHandlerMethod#getMethodArgumentValues
public class InvocableHandlerMethod extends HandlerMethod {// 请求入参解析器集合private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {this.resolvers = argumentResolvers;}// 获取方法参数集protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {if (ObjectUtils.isEmpty(getMethodParameters())) {return EMPTY_ARGS;}MethodParameter[] parameters = getMethodParameters();Object[] args = new Object[parameters.length];// 遍历每个参数for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}// 先判断是否存在支持的解析器if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {// 针对每个参数进行解析args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) { ...... }}return args;}}
上述部分代码展示的很清楚了,根据每个请求入参,遍历 请求入参解析器集合 针对不用类型的入参类型,进行解析器解析。
3.2、响应结果解析
分析解析器集合
与 请求入参解析器雷同,在响应结果解析中,响应结果解析器放在了集合 HandlerMethodReturnValueHandlerComposite 中。
HandlerMethodReturnValueHandlerComposite 类图如下
先看看接口定义 HandlerMethodReturnValueHandler
public interface HandlerMethodReturnValueHandler {// 判断是否符合解析条件boolean supportsReturnType(MethodParameter returnType);// 对响应结果进行操作void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;}
在接口规范定义中,定义了两个方法
- HandlerMethodReturnValueHandler#supportsReturnType
响应结果是否符合解析器,解析条件 - HandlerMethodReturnValueHandler#handleReturnValue
通过向模型添加属性并将视图或标志设置为true来处理给定的返回值,以指示直接处理了响应。
再来看看 HandlerMethodReturnValueHandlerComposite 相关代码
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {// 维护的响应结果解析器 集合private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();// 解析器集合的增删改操作public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler handler) { ...... }public List<HandlerMethodReturnValueHandler> getHandlers() { ...... }boolean supportsReturnType(MethodParameter returnType){ ...... }void handleReturnValue( ...... ) throws Exception { ...... }@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}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;}}
HandlerMethodReturnValueHandlerComposite 从接口的定义上来说也是一个 响应结果解析器 ,不过它是一个解析器集合,相当于一个代理人,委托人,主要功能如下:
1、 维护了 List 集合 returnValueHandlers ,用来存放 HandlerMethodReturnValueHandler 响应结果解析器
2、 提供增删改的操作进行 List 集合的维护。
3、 业务操作
- a、supportsReturnType 判断操作,委派给 List 中各个
HandlerMethodReturnValueHandler的 supportsReturnType 去判断 - b、handleReturnValue 解析操作
解析器如何生效
再次回到 HttpRequestHandlerAdapter#invokeHandlerMethod 方法。
该方法构建 ServletInvocableHandlerMethod 对象并设置相关参数,包括 请求参数解析器集合 HandlerMethodArgumentResolverComposite ,在环境和参数准备完成之后,调用 ServletInvocableHandlerMethod#invokeAndHandle 进行方法请求调用。
聚焦 ⑥ ServletInvocableHandlerMethod#invokeAndHandle
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 请求参数通过解析器解析之后执行方法调用,并返回结果Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// ⑨ 结果集处理this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}}
四、总结

Spring MVC 依赖于 Servlet 容器进行启动,所有请求的处理都由 DispatcherServlet 进行处理。
在请求参数相关格式的处理上,Spring MVC 通过过滤连 HandlerMethodArgumentResolver 进行处理。
