该篇博客介绍了调用的部分逻辑,这篇主要介绍了实现Controller接口和实现HttpRequestHandler接口这两种方式的调用,这两种方式比较简单,就是通过强转,然后调用目标方法即可,麻烦的是下面这种,通过@Controller注解声明的controller,在这篇博客中会仔细介绍

对于通过@Controller、@RequestMapping()这种通过注解,声明handlerMethod的方式,会分为以下三步:
1.对请求中的参数进行解析
2.调用目标方法
3.对返回参数进行解析

我们首先看下调用链,我们要说的代码,之前的解析逻辑就是下面这个调用链

org.springframework.web.servlet.DispatcherServlet#doDispatchorg.springframework.web.servlet.HandlerAdapter#handleorg.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handleorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternalorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethodorg.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle较为核心的方法,就在这里
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {/*** 调用实际的目标方法* 在这个方法中完成了两步操作*    1.对参数的解析*   2.对目标方法的调用*/Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);/*** 下面这里是对返回参数进行解析,举例:* @ResponseBody 注解就是在下面这里被处理的*/if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {/*** 处理返回结果* 如果返回结果是对象,并且加了 @ResponseBody的注解,那么spring内部维护了一个 HttpMessageConverters*/this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);}throw ex;}
}

一、对参数的处理

我上面有写过,上面的一个方法中,完成了三个步骤的处理,我们先来看对参数的解析


@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {/*** 第一步:解析入参*/Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +"' with arguments " + Arrays.toString(args));}/*** 第二步调用对应的方法,获取返回值*/Object returnValue = doInvoke(args);if (logger.isTraceEnabled()) {logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +"] returned [" + returnValue + "]");}return returnValue;
}

我们接着看具体的解析逻辑

下面这个方法,里面的逻辑就是这样的
1.获取到所有的入参
2.依次去解析每个入参,判断哪个参数解析器可以解析,然后进行解析

/*** Get the method argument values for the current request.* 从request中获取到当前方法的入参,也就是args*/
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {/*** 获取到所有的参数名*/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);/*** 下面这行代码,看注释的意思是:从提供的参数列表中获取对应的值,但是从前面的调用链中会发现,入参的providedArgs是null*/args[i] = resolveProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}/*** 遍历所有的参数解析器,看哪个解析器可以解析,如果参数解析器返回true,就表示可以解析* 如果我们要扩展参数解析器,就需要看下这里的逻辑* 需要学习下这里的argumentResolvers是在哪里赋值的*/if (this.argumentResolvers.supportsParameter(parameter)) {try {/*** 这里就是用parameter对应的解析器去解析该参数*/args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);continue;}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);}throw ex;}}if (args[i] == null) {throw new IllegalStateException("Could not resolve method parameter at index " +parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));}}return args;
}

下面这个方法就是具体判断的逻辑,举个例子:
比如我自定义了一个注解@XXXX,然后自定义一个参数解析器,就是用来解析这个自定义的注解,那就只需要在supportsParameter方法中判断,参数是否添加了@XXXX注解就可以,这个后面单独会介绍


/*** 遍历所有的参数处理器,找到处理该parameter的处理器,然后存入到map集合中,* 第二次获取处理该参数的处理器时,就无须再次遍历所有的support方法,直接从map缓存中获取即可* @param parameter* @return*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {if (logger.isTraceEnabled()) {logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +parameter.getGenericParameterType() + "]");}/*** 核心方法就是这里,上面会遍历所有的参数解析器,依次去调用对应的supports方法,判断是否可以处理parameter* 如果可以,就返回true,然后会把对应的methodArgumentResolver和对应的parameter关联起来*/if (methodArgumentResolver.supportsParameter(parameter)) {result = methodArgumentResolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;
}

下面这里是具体去处理的方法

@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {/*** 这个方法是实际解析参数的逻辑,首先会先获取到参数对应的解析器*/HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");}/*** 这里调用的就是处理的方法*/return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

二、调用目标方法

调用这里就不仔细说了,里面的逻辑也没多少

Object returnValue = doInvoke(args);

doInvoke中会调用method.invoke(obj,args);方法去调用目标方法

三、对返回值的处理

对返回值的出来,核心的方法在这里:

handleReturnValue
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {/*** 拿到返回值之后,判断当前返回的对象,需要由哪个handler来处理,如果是json的话,是requestResponseBodyMethodProcessor来处理的*/HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}/*** 这里是调用returnValueHandler的处理方法*/handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

其实对返回值的处理逻辑,思想和参数解析是基本一样的,先判断解析类是否支持解析该返回值,如果支持解析该返回值的话,就调用对应的解析方法


@Nullable
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;
}

我们以@ResponseBody注解为例来看下,在得到返回值之后,就判断方法或者类上是否有添加@ResponseBody注解

/*** 处理@ResponseBody注解修饰的方法,在获取到方法的返回值之后,会通过该类对返回值进行处理* @param returnType the method return type to check* @return*/
@Override
public boolean supportsReturnType(MethodParameter returnType) {return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||returnType.hasMethodAnnotation(ResponseBody.class));
}

以上就是对方法参数的解析、方法调用、返回值解析的逻辑,其实也比较简单:
1、首先遍历所有的参数解析器,判断是否支持解析该参数,如果支持,就解析
2、通过method.invoke()调用目标方法
3、对返回值进行判断处理,和参数解析的思想一致,这里用到的应该就是适配器模式

springmvc源码-参数解析相关推荐

  1. 筑基期第一式:SpringMVC源码解析

    文章目录 SpringMVC源码解析 SPI机制 案例 SpringMVC中SPI的使用 初始化IOC容器与九大组件 初始化容器 初始化九大组件 小结 SpringMVC如何处理一个请求 doDisp ...

  2. 机器学习算法源码全解析(三)-范数规则化之核范数与规则项参数选择

    前言 参见上一篇博文,我们聊到了L0,L1和L2范数,这篇我们絮叨絮叨下核范数和规则项参数选择.知识有限,以下都是我一些浅显的看法,如果理解存在错误,希望大家不吝指正.谢谢. 机器学习算法源码全解析( ...

  3. SpringMVC源码阅读:过滤器

    SpringMVC源码阅读:过滤器 目录 1.前言 2.源码分析 3.自定义过滤器 3.1 自定义过滤器继承OncePerRequestFilter 3.2 自定义过滤器实现Filter接口 4.过滤 ...

  4. SpringMVC源码分析(4)剖析DispatcherServlet重要组件

    简单介绍了一个请求的处理过程, 简略描述了调用过程,并没有涉及过多细节,如url匹配,报文解析转换等. <SpringMVC源码分析(2)DispatcherServlet的初始化>:介绍 ...

  5. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  6. springMvc源码刨析笔记

    springMvc源码刨析笔记 MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 We ...

  7. yolov3之pytorch源码解析_springmvc源码架构解析之view

    说在前面 前期回顾 sharding-jdbc源码解析 更新完毕 spring源码解析 更新完毕 spring-mvc源码解析 更新完毕 spring-tx源码解析 更新完毕 spring-boot源 ...

  8. java注解返回不同消息,SpringMVC源码剖析5:消息转换器HttpMessageConverter与@ResponseBody注解...

    转自 [SpringMVC关于json.xml自动转换的原理研究[附带源码分析]](https://www.cnblogs.com/fangj... 部分代码会放在我的的Github:https:// ...

  9. spring boot 源码_springboot源码架构解析listener

    说在前面 前期回顾 sharding-jdbc源码解析 更新完毕 spring源码解析 更新完毕 spring-mvc源码解析 更新完毕 spring-tx源码解析 更新完毕 spring-boot源 ...

  10. SpringMVC源码剖析(五)-消息转换器HttpMessageConverter

    概述 在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新 ...

最新文章

  1. 资料分享:数学建模资料分享 -- 神经网络部分
  2. 吴恩达机器学习笔记31-梯度检验(Gradient Checking)
  3. 阿里JAVA 开发手册----整理
  4. 一起从头学习Flex
  5. 解读dbcp自动重连那些事---转载
  6. SHLL脚本从1到100自加
  7. 日本专家给出的存钱高招(图)
  8. Java中implies_boolean implies(Permission p)
  9. mac 设置终端样式_如何将终端样式设置为freeCodeCamp或任何您想要的样式
  10. PSPad 免费文本编辑器推荐
  11. 解读8大场景下Kunpeng BoostKit 使能套件的最佳能力和实践
  12. express+mysqle
  13. 1056. 组合数的和(15)-PAT乙级真题
  14. VARCHART XGantt甘特图具有更多功能的HTML5 / Gantt图表的可视计划小部件
  15. 图像处理——人脸情绪识别(python卷积神经网络)
  16. win10与ubuntu20.04双系统启动引导问题
  17. 使用Arduino和DS12C887芯片制作电子时钟(附Arduino源代码)
  18. 这么多处理器(CPU/SOC)牌子,到底哪家强
  19. 关于ckeditor富文本编辑器上传图片返回“不正确的服务器响应”的解决方案
  20. new指针后,地址相同

热门文章

  1. 容器技术Docker K8s 52 边缘容器服务(ACK@Edge)-边缘计算和边缘容器
  2. 容器技术Docker K8s 4 容器编排技术基础-Kubernetes
  3. StarUML接口视图修改为类的形式
  4. 如何安装suse linux操作系统,SUSE Linux Enterprise Server 11 SP3安装教程详解
  5. Java 最长递增子序列_最长递增子序列问题 Java
  6. 2021-09-03Dien
  7. 2021-09-02语义分割 实例分割 全景分割
  8. 字符串中拼接v-for_C#中几种拼接字符串的方法
  9. IDEA连接Spark集群执行Scala程序
  10. gamma 函数的 LaTeX 代码