SpringMVC源码阅读:Controller中参数解析

1.前言SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧本文将通过源码(基于Spring4.3.7)分析,弄清楚Controller是如何匹配我们传入的参数,并定义简单的参数解析器2.源码分析demo源码在这里,回到DispatcherServlet的doDispatch方法,Dispatc...

SpringMVC源码阅读:Controller中参数解析

1.前言

SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧

本文将通过源码(基于Spring4.3.7)分析,弄清楚Controller是如何匹配我们传入的参数,并定义简单的参数解析器

2.源码分析

demo源码在这里,回到DispatcherServlet的doDispatch方法,DispatchServlet分析见SpringMVC源码阅读:核心分发器DispatcherServlet

doDispatch方法943行获取了HandlerAdapter,ctrl h打开类继承图,找到RequestMappingHandlerAdapter,RequestMappingHandlerAdapter支持HandlerMethod的方法参数和返回类型,HandlerMethod是3.1版本引入的,为参数、返回值和注解提供便捷的封装

在RequestMappingHandlerAdapter的invokeHandlerMethod方法中,设置ArgumentResolver和ReturnValueHandler

798行和799行给RequestMappingHandlerAdapter定义的ArgumentResolver和ReturnValueHandler赋值,4.2版本以前在createRequstMapping方法,此方法在4.2已被删除

827行ServletInvocableHandlerMethod调用invokeAndHandle方法,通过定义的HandlerMethodReturnValueHandler处理返回值,点开invokeAndHandle方法进入ServletInvocableHandlerMethod类

116行处理通过HandlerMethodArgumentResolver来解析参数,132~133行使用注册过的HandlerMethodReturnValueHandler

afterPropertiesSet方法实现了InitializingBean接口初始化了Handler和Resolver,简单地说,启动服务才会运行afterPropertiesSet

517行设置ArgumentResolver,525行设置ReturnValueHandler

点看getDefaultArgumentResolvers方法,看看它到底做了什么

getDefaultArgumentResolvers方法把各种HandlerMethodArgumentResolver放入List并返回

同理,getDefaultReturnValueHandlers方法把各种HandlerMethodReturnValueHandler放入List并返回

现在在回到ServletInvocableHandlerMethod类,我们发现了returnValueHandlersHandlerMethodReturnValueHandlerComposite类型的,神秘的HandlerMethodReturnValueHandlerComposite是什么?

查看类实现图,我们发现HandlerMethodReturnValueHandlerComposite继承HandlerMethodReturnValueHandler

我们可以看到,HandlerMethodReturnValueHandlerComposite类里有HandlerMethodReturnValueHandler类型的list,做过树结构的园友们应该知道,这里用到了组合模式,即类包含自身对象组。但是呢,它的类名后面加上了Composite,不能严格意义上说是组合模式,可以说是组合模式的变种,因为HandlerMethodReturnValueHandler是个Interface,所以不是严格意义上的“组合模式”

我们用同样的思路寻找与HandlerMethodArgumentResolver对应的Composite类,我在ServletInvocableHandlerMethod没有找到HandlerMethodArgumentResolverComposite,(在4.3版本之前可以在ServletInvocableHandlerMethod找到),不用担心,使用绝招

快捷键ctrl shift r,用idea强大的全局搜索来找HandlerMethodArgumentResolverComposite的踪迹

这里注意一下,全局搜索选择Scope,才可以在文件所有路径下搜索(包括Maven源码包)

最后我们看到了HandlerMethodArgumentResolverComposite在InvocableHandlerMethod出现,这个类名觉得有些眼熟吧,它是ServletInvocableHandlerMethod的父类

ServletInvocableHandlerMethod调用InvocableHandlerMethod的invokeForRequest方法中使用了HandlerMethodArgumentResolverComposite

打开HandlerMethodArgumentResolverComposite,和HandlerMethodReturnValueHandlerComposite类似,使用组合模式的变种

好了,参数解析基本流程完毕,我们现在来具体看看支持和参数相映射的注解的参数解析类,对着HandlerMethodArgumentResolver按ctrl h

可以看到庞大的类继承图,我们看支持@RequestBody的RequestResponseBodyMethodProcessor类

可能会有园友好奇,为什么我知道RequestResponseBodyMethodProcessor类支持@RequestBody?

一个简便的方法是直接看类名,开源项目Spring的代码质量非常高,它们的类名言简意赅,看类名大概就知道它是做什么的;类名如果看不出来,就点进去看注释,注释很规范、详细

打开RequestResponseBodyMethodProcessor类

支持带有@RequestBody的参数,支持带有@ResponseBody的返回值

写一个方法进行测试

@RequestMapping(value = "/testRb",produces={"application/json; charset=UTF-8"},method = RequestMethod.POST) @ResponseBody public Employee testRb(@RequestBody Employee e) {  return e; }

http://localhost:8080/springmvcdemo/test/testRb,传入参数为{"age":1,"id":2},我用的Postman测试请求,直接浏览器地址栏输入,默认Get请求会报错,不嫌麻烦可以自己手写Ajax,参数类型设置成Json测试

header写成application/json,请求类型写POST

Body传入Json格式参数

现在我们进入resolveArgument方法

127行获取参数信息,128行调用readWithMessageConverters方法获取参数值

131行创建WebDataBinder,用于校验数据格式是否正确

点开128行readWithMessageConverters方法,看看它做什么

148行获取请求信息,如头信息

我们看到Content-Type正是我们在Postman中设置的"application/json"

150行获取参数,调用父类AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters方法,父类方法用于从请求信息中读取方法参数值

152行查看参数注解是否是@RequestBody

继续深入,进入AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters方法

167行从Headers取得Content-Type

172~175行如果Content-Type为空,默认给我们Content-Type设置"application/octet-stream"

185行获取Http请求方法

191行用消息转换器读取请求体

接下来,我们分析下常用的@RequestParam注解是如何处理参数的

用这个测试方法

 @RequestMapping("/auth") public String auth(@RequestParam String username, HttpServletRequest req) {  req.getSession().setAttribute("loginUser", username);  return "redirect:/"; }

找到RequestParamMethodArgumentResolver类,该类的核心方法是resolveName方法

158行获取请求信息

159行获取MultipartHttpServletRequest请求信息,用于文件上传

175行获取参数值

打断点我发现,在RequestParamMethodArgumentResolver的父类AbstractNamedValueMethodArgumentResolver中,resolveArgument方法会先执行。后续我们自定义的参数解析器主要就是重写resolveArgument方法

97行获取参数名称

103行调用resolveName方法获取参数值,该方法被AbstractNamedValueMethodArgumentResolver子类RequestParamMethodArgumentResolver实现,刚才我们已经分析过

我再说下其他常用的HandlerArgumentResolver实现类,就不源码分析了,有时间我会补充上,园友可以自行打断掉调试查看之

1.PathVariableMethodArgumentResolver

支持带有@PathVariable注解的参数,用来获得请求url中的动态参数

2.MatrixVariableMethodArgumentResolver

支持带有@MatrixVariable注解的参数,顾名思义,矩阵变量,多个变量可以使用“;”分隔

3.RequestParamMethodArgumentResolver

支持带有@RequestParam注解的参数,也支持MultipartFile类型的参数,本文已分析

4.RequestResponseBodyMethodProcessor

<

源文地址:https://www.guoxiongfei.cn/cntech/9332.html