[SpringMVC 源码] SpringMVC 中 HTTP 请求与响应原理
来源:互联网 发布:做淘宝直播怎么找商家 编辑:程序博客网 时间:2024/06/04 21:02
一、现象描述
通过 ajax post 方式调用 SpringMVC 接口时,返回 json 对象一般有 Object
、String
两种。而返回对象定义为String
类型时,发现中文乱码。现象截图如下:
前端调用代码如下:通过传入的 url 来区分
function fSave(url) { var obj = {}; obj['cateId'] = $("input[name=cateId]").val(); obj['cateName'] = $("input[name=cateName]").val(); $.ajax({ url: url, method: 'post', contentType: 'application/json', // 这句不加出现415错误:Unsupported Media Type data: JSON.stringify(obj), // 以json字符串方式传递 success: function(data) { console.log(data); }, error: function(data) { console.log("error..."); console.log(data); } });}
服务端接口定义如下:
@RequestMapping(value = "/category/save-by-map", method = RequestMethod.POST)@ResponseBodypublic String saveByMap(@RequestBody Map<String, Object> valMap) { return valMap.toString();}@RequestMapping(value = "/category/save-by-map-2", method = RequestMethod.POST)@ResponseBodypublic Map<String, Object> saveByMap2(@RequestBody Map<String, Object> valMap) { return valMap;}
二、HTTP 请求与响应原理
SpringMVC 版本 4.1.1.RELEASE
流程图如下:
占坑。。
UML时序图如下:
占坑。。
概述:在 ServletInvocableHandlerMethod
类的 invokeAndHandle()
方法中,SpringMVC
解析请求参数,并调用相应的方法;对方法的返回值进行相应的处理后返回。
具体细节如下:
1. 解析请求参数并调用相应的接口方法,获得返回值:invokeForRequest()
在该方法中,调用 getMethodArgumentValues()
方法获取方法输入参数,调用 invoke()
方法调用相应的接口方法,最终返回方法返回值。invokeForRequest()
方法截图如下:
1.1 解析请求参数: getMethodArgumentValues()
在该方法中,主要调用HandlerMethodArgumentResolverComposite
类的 resolveArgument
方法,根据输入参数取的对应的 HandlerMethodArgumentResolver
策略类(策略模式)解析方法参数:
调用方法参数处理策略类的 resolveArgument()
方法:
此处示例输入参数有注解 @RequestBody
,因此调用的策略类为:RequestResponseBodyMethodProcessor
,对应的还有 @RequestParam
对应的 RequestParamMethodArgumentResolver
策略类等。而在该类的resolveArgument()
方法的实现中,调用了 readWithMessageConverters()
方法:
而该方法中,则调用了其父类 AbstractMessageConverterMethodArgumentResolver
的 readWithMessageConverters()
方法:
该方法中,则调用具体的 HttpMessageConverter
类的read()
方法对方法参数进行转换,本示例中由于输入的参数为 Object
类型,因此调用的为 AbstractJackson2HttpMessageConverter
类的 read()
方法:
如输入的参数为 String
类型,则会调用相应的 StringHttpMessageConverter
类:
1.2 根据参数调用具体的处理方法,并取得返回值: invoke()
该方法中,主要通过反射机制调用响应的方法并取得返回值:
getBridgedMethod()
方法返回值为:
继续往下走,进入具体的方法调用:
1.3 返回返回值: return returnValue;
此时的 returnValue 为 Object
类型:
2. 调用 returnValueHandlers
的handleReturnValue()
方法
此处,returnValueHandlers
即HandlerMethodReturnValueHandlerComposite
类,调用该类的handleReturnValue()
方法,该方法中可判断哪个返回值处理策略类 HandlerMethodReturnValueHandler
,然后调用 handleReturnValue() 方法
处理返回值:
2.1 获取具体的返回值处理类: getReturnValueHandler()
该方法根据具体的返回值处理类是否支持方法指定的返回类型(returnType: @ResponseBody, ModelAndView 等) 判断,此处由于返回值为 @ResponseBody
,因此调用的策略类为: RequestResponseBodyMethodProcessor
(如果定义返回类型为ModelAndView
,则返回值处理器为ModelAndViewMethodReturnValueHandler
):
2.2 调用具体的方法返回值处理类: handleReturnValue()
RequestResponseBodyMethodProcessor
类的 handleReturnValue()
方法中,调用父类AbstractMessageConverterMethodProcessor
的writeWithMessageConverters()
方法,根据返回值类型调用相应的消息转换器 HttpMessageConverter
类,此处与前面 1.1 中,调用 readWithMessageConverters()
方法类似:
注:方法中的 getProducibleMediaTypes()
方法会获取 @RequestMapping(value = "/save-by-model", method = RequestMethod.POST, produces = "text/plain;charset=utf-8")
指定的 produces 的值:
遍历消息转换器HttpMessageConverter
列表,通过方法 canWrite()
判断该消息转换器是否可操作返回的 Class
类型:
取第一个匹配的消息转换器类,调用该类的 write()
方法:
注:此处调用 write()
方法之前会调用 adviceChain.invoke()
方法对返回值进行处理(AOP 机制)。
此处消息转换器类为 MappingJackson2HttpMessageConverter
类,往下执行,调用父类的 write()
方法,再调用具体消息转换器类的 writeInternal()
方法,此处为调用父类的 AbstractJackson2HttpMessageConverter
的 writeInternal()
方法:
再调用具体的 writePrefix(), writeSuffix()
方法,此处设计模式为 模板方法模式,MappingJackson2HttpMessageConverter
的相应实现如下:
三、解决方案
根据第二点的原理解析,可以发现中文乱码出现的原因为:处理返回值时,由于返回值类型为 String
, 即调用了 StringHttpMessageConverter
类对返回数据进行转换,而给消息转换器支持的默认字符编码集为 ISO-8859-1
:
而返回 Object
类型无中文乱码,是因为该返回值类型调用的消息转换器为 MappingJackson2HttpMessageConverter
而该消息转换器支持的字符编码为 UTF-8
,因此无乱码:
综上,解决方案为指定具体的返回值, 如:
@RequestMapping(value = "/save-by-map", method = RequestMethod.POST, produces = "text/plain;charset=utf-8")@ResponseBodypublic String saveByMap(@RequestBody Map<String, Object> valMap) { return valMap.toString();}
或者通过 xml 设置: springmvc @ResponseBody 返回中文乱码
输出为:
注:HttpMessageConverter
的 canRead(), canWrite()
方法根据: 具体的消息转换类是否支持返回值类型以及相应的编码集是否匹配,如常规的消息抓换抽象类中:
而使用 Jackson
的消息转换抽象类中,canRead()
方法以输入参数类型转换而来的的 Jackson JavaType
是否可被反序列化以及编码集是否匹配,canWrite()
方法以返回值类型转换而来的的 Jackson JavaType
是否可被序列化以及编码集是否匹配来判断:
- [SpringMVC 源码] SpringMVC 中 HTTP 请求与响应原理
- springmvc中的http请求参数与响应
- SpringMVC 请求、响应流程
- springmvc httpclient 请求 响应
- SpringMVC解析请求响应请求过程-源码分析
- SpringMVC之http的请求响应模型(三)
- SpringMVC之http请求头和响应头(四)
- SpringMVC源码分析,springMVC原理
- springmvc http请求 406
- SpringMvc请求原理-yellowcong
- springmvc 请求响应时报异常
- SpringMVC实现JSON数据的请求与响应
- http请求后台springMvc中获取不到请求参数
- http请求与响应
- HTTP请求与响应
- Http请求与响应
- http请求与响应
- http请求与响应
- markdown编辑器语法
- ubuntu16.04安装opencv找不到cv2.so问题解决
- java关于多线程相关问题
- 2017/12/19 bootstrap登陆界面样式
- SpringBoot启动流程简析(二)
- [SpringMVC 源码] SpringMVC 中 HTTP 请求与响应原理
- Spring Security 入门教程 -01- Hello World
- 渐行渐远
- UE4中制作高大上的相机动画
- JAVA设计模式之单例模式
- Androi_SwipeRefreshLayout上拉刷新、AVLoadingIndicatorView加载动画
- 翻译:macOS10.12.2 local_privilage_Escalation
- 死磕红皮书(在HTML中使用Javascript)
- CSS隐藏元素的几种方法