一、回顾

《Spring-MVC》 中讲解了 Spring MVC 的工作机制。

第一步、 请求 Request 进入到 Spring 应用中,统一由分发器 DispatcherServlet 进行接收处理。

第二步、 DispatcherServlet 根据请求路径,从 HandlerMapping 中获取对应的 controller ,并将结果返回为 DispatcherServlet。

第三步、 DispatcherServlet 根据 RequestMapping 返回的 Handler 去 HandlerAdapter 中获取对应适配的结果返回给 DispatcherServlet,如:ModelAndView

第四步、 DispatcherServlet 根据返回的 ModelAndView 到 ViewResolver 中进行解析,最后得到响应 View

二、Spring MVC 简单案例

  1. @Controller
  2. public class SpringMvcController {
  3. @RequestMapping("/sys/request-body")
  4. @ResponseBody
  5. public SysDict requestBody(@RequestBody SysDict sysDict){
  6. return sysDict;
  7. }
  8. @RequestMapping("/sys/obj")
  9. @ResponseBody
  10. public SysDict obj(SysDict sysDict){
  11. return sysDict;
  12. }
  13. @RequestMapping("/sys/request-param")
  14. @ResponseBody
  15. public String requestParam(@RequestParam String sysDict){
  16. return sysDict;
  17. }
  18. @RequestMapping("/sys/date")
  19. @ResponseBody
  20. public Date testDate(@DateTimeFormat(pattern = "yyyy-HH-mm HH:mm:ss") Date date){
  21. return date;
  22. }
  23. }

以上的几个接口中,各自方法接收了不同的参数类型,并通过譬如:@RequestParam @DateTimeFormat 等方式对参数进行解析赋值。

问题

Spring MVC 在请求参数与方法绑定前如何对参数进行解析?且在返回结果后对结果集进行解析?

通过 Spring MVC 的工作流程,能够直接的猜测Spring MVC 参数解析赋值的操作在 第三步 HandlerAdapter 的执行过程中。

三、源码解析

Spring MVC 在请求参数与方法绑定前如何对参数进行解析?且在返回结果后对结果集进行解析?

Spring MVC 构造 ServletInvocableHandlerMethod

在 Spring MVC 中 RequestMappingHandlerAdapter 是全局的请求适配器,针对每一次请求,Spring MVC 会构造 ServletInvocableHandlerMethod 进行请求调用。 而 请求参数解析器集合 和 响应结果解析器集合 在构造 ServletInvocableHandlerMethod 时会作为属性设置到 ServletInvocableHandlerMethod 中。

聚焦 DispatcherServlet#doDispatch 代码

  1. public class DispatcherServlet extends FrameworkServlet {
  2. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  3. // Determine handler adapter for the current request.
  4. // 获取 HandlerAdapter
  5. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  6. // Actually invoke the handler.
  7. // 执行 HandlerAdapter#handle
  8. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  9. }
  10. }

Spring MVC 中 DispatcherServlet 进行请求处理的主要两个步骤:

  • 步骤一:获取 HandlerAdapter (默认实现:RequestMappingHandlerAdapter)
  • 步骤二:执行 HandlerAdapter#handle

下面我们来看看 默认实现:RequestMappingHandlerAdapter#handler 方法的实现

RequestMappingHandlerAdapter#handler

02.png

主要的执行逻辑方法为 RequestMappingHandlerAdapter#invokeHandlerMethod

聚焦 RequestMappingHandlerAdapter#invokeHandlerMethod

  1. public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
  2. protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  3. ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
  4. // 请求数据解析类集合
  5. if (this.argumentResolvers != null) {
  6. invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  7. }
  8. // 响应数据解析类集合
  9. if (this.returnValueHandlers != null) {
  10. invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  11. }
  12. }
  13. }

在该方法中对请求执行对象 ServletInvocableHandlerMethod 进行属性赋值,其中主要的两个属性对象:

  • 请求参数解析器集合

    HandlerMethodArgumentResolverComposite argumentResolvers

  • 响应结果解析器集合

    HandlerMethodReturnValueHandlerComposite returnValueHandlers

3.1、请求参数解析分析

分析解析器集合

分析请求参数解析器集合 HandlerMethodArgumentResolverComposite

HandlerMethodArgumentResolverComposite 类图如下:
03.png
先来看看接口定义 HandlerMethodArgumentResolver

  1. // @since 3.1
  2. public interface HandlerMethodArgumentResolver {
  3. // 判断方法参数,是否符合解析条件
  4. boolean supportsParameter(MethodParameter parameter);
  5. // 进行解析
  6. Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory);
  7. }

方法参数解析 接口定义了两个方法:

  • HandlerMethodArgumentResolver#supportsParameter
    判定方法参数是否满足此解析器的条件
  • HandlerMethodArgumentResolver#resolveArgument
    进行参数解析

在这里,大体可以猜得出来,这个请求入参解析器,会针对每一个请求入参进行 supportsParameter 判断,符合条件的就进行 resolveArgument 解析操作。

再来看看 HandlerMethodArgumentResolverComposite 具体实现代码,如下:

  1. // 请求入参解析器集合
  2. public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
  3. // 维护的 入参解析器
  4. private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();
  5. // 维护的缓存,用于提高性能
  6. private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
  7. new ConcurrentHashMap<>(256);
  8. // 针对 List<HandlerMethodArgumentResolver> 的增删改查操作
  9. public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) { ...... }
  10. public List<HandlerMethodArgumentResolver> getResolvers() { ...... }
  11. public void clear() { ...... }
  12. public Object resolveArgument( ......){ ...... }
  13. private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { ...... }
  14. // 执行解析
  15. @Override
  16. public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  17. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  18. // 获取解析并执行解析操作
  19. HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
  20. if (resolver == null) {
  21. throw new IllegalArgumentException(
  22. "Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
  23. " supportsParameter should be called first.");
  24. }
  25. return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  26. }
  27. // 获取匹配的解析执行器
  28. private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
  29. // 从缓存中取
  30. HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
  31. if (result == null) {
  32. // 缓存没有,遍历 解析器集合,分别调用 supportsParameter 获取
  33. for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
  34. if (methodArgumentResolver.supportsParameter(parameter)) {
  35. result = methodArgumentResolver;
  36. this.argumentResolverCache.put(parameter, result);
  37. break;
  38. }
  39. }
  40. }
  41. return result;
  42. }
  43. }

HandlerMethodArgumentResolverComposite 从接口定义来说也是一个 请求入参解析器,不过它是一个解析器集合,相当于一个代理人,或者委托人,主要功能信息如下:

1、维护了一个 List 集合 argumentResolvers 用来存放 HandlerMethodArgumentResolver 解析器。

2、提供增删改的操作进行 List 集合的维护。

3、业务操作;

  • a、supportsParameter 判断操作,委派给 List 中的解析器完成
  • b、resolveArgument 解析操作,也委派给 List 中的解析器完成

解析器如何生效

再次回到 HttpRequestHandlerAdapter#invokeHandlerMethod 方法。

该方法构建 ServletInvocableHandlerMethod 对象并设置相关参数,包括 请求参数解析器集合 HandlerMethodArgumentResolverComposite ,在环境和参数准备完成之后,调用 ServletInvocableHandlerMethod#invokeAndHandle 进行方法请求调用。
04.png
聚焦 ⑦ InvocableHandlerMethod#getMethodArgumentValues

  1. public class InvocableHandlerMethod extends HandlerMethod {
  2. // 请求入参解析器集合
  3. private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
  4. public void setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {
  5. this.resolvers = argumentResolvers;
  6. }
  7. // 获取方法参数集
  8. protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  9. Object... providedArgs) throws Exception {
  10. if (ObjectUtils.isEmpty(getMethodParameters())) {
  11. return EMPTY_ARGS;
  12. }
  13. MethodParameter[] parameters = getMethodParameters();
  14. Object[] args = new Object[parameters.length];
  15. // 遍历每个参数
  16. for (int i = 0; i < parameters.length; i++) {
  17. MethodParameter parameter = parameters[i];
  18. parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
  19. args[i] = findProvidedArgument(parameter, providedArgs);
  20. if (args[i] != null) {
  21. continue;
  22. }
  23. // 先判断是否存在支持的解析器
  24. if (!this.resolvers.supportsParameter(parameter)) {
  25. throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
  26. }
  27. try {
  28. // 针对每个参数进行解析
  29. args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
  30. }
  31. catch (Exception ex) { ...... }
  32. }
  33. return args;
  34. }
  35. }

上述部分代码展示的很清楚了,根据每个请求入参,遍历 请求入参解析器集合 针对不用类型的入参类型,进行解析器解析。

3.2、响应结果解析

分析解析器集合

与 请求入参解析器雷同,在响应结果解析中,响应结果解析器放在了集合 HandlerMethodReturnValueHandlerComposite 中。

HandlerMethodReturnValueHandlerComposite 类图如下
05.png
先看看接口定义 HandlerMethodReturnValueHandler

  1. public interface HandlerMethodReturnValueHandler {
  2. // 判断是否符合解析条件
  3. boolean supportsReturnType(MethodParameter returnType);
  4. // 对响应结果进行操作
  5. void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
  6. }

在接口规范定义中,定义了两个方法

  • HandlerMethodReturnValueHandler#supportsReturnType
    响应结果是否符合解析器,解析条件
  • HandlerMethodReturnValueHandler#handleReturnValue
    通过向模型添加属性并将视图或标志设置为true来处理给定的返回值,以指示直接处理了响应。

再来看看 HandlerMethodReturnValueHandlerComposite 相关代码

  1. public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
  2. // 维护的响应结果解析器 集合
  3. private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
  4. // 解析器集合的增删改操作
  5. public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler handler) { ...... }
  6. public List<HandlerMethodReturnValueHandler> getHandlers() { ...... }
  7. boolean supportsReturnType(MethodParameter returnType){ ...... }
  8. void handleReturnValue( ...... ) throws Exception { ...... }
  9. @Override
  10. public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
  11. ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  12. HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
  13. if (handler == null) {
  14. throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
  15. }
  16. handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
  17. }
  18. private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
  19. boolean isAsyncValue = isAsyncReturnValue(value, returnType);
  20. for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
  21. if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
  22. continue;
  23. }
  24. if (handler.supportsReturnType(returnType)) {
  25. return handler;
  26. }
  27. }
  28. return null;
  29. }
  30. }

HandlerMethodReturnValueHandlerComposite 从接口的定义上来说也是一个 响应结果解析器 ,不过它是一个解析器集合,相当于一个代理人,委托人,主要功能如下:

1、 维护了 List 集合 returnValueHandlers ,用来存放 HandlerMethodReturnValueHandler 响应结果解析器

2、 提供增删改的操作进行 List 集合的维护。

3、 业务操作

  • a、supportsReturnType 判断操作,委派给 List 中各个 HandlerMethodReturnValueHandler 的 supportsReturnType 去判断
  • b、handleReturnValue 解析操作

解析器如何生效

再次回到 HttpRequestHandlerAdapter#invokeHandlerMethod 方法。

该方法构建 ServletInvocableHandlerMethod 对象并设置相关参数,包括 请求参数解析器集合 HandlerMethodArgumentResolverComposite ,在环境和参数准备完成之后,调用 ServletInvocableHandlerMethod#invokeAndHandle 进行方法请求调用。
06.png
聚焦 ⑥ ServletInvocableHandlerMethod#invokeAndHandle

  1. public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
  2. public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
  3. // 请求参数通过解析器解析之后执行方法调用,并返回结果
  4. Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  5. // ⑨ 结果集处理
  6. this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  7. }
  8. }

通过上述能够知道解析器的处理,更多情况,这里不展开。

四、总结

04-01.png

Spring MVC 依赖于 Servlet 容器进行启动,所有请求的处理都由 DispatcherServlet 进行处理。

在请求参数相关格式的处理上,Spring MVC 通过过滤连 HandlerMethodArgumentResolver 进行处理。