SpringMVC解析JSON请求数据
来源:互联网 发布:java并发编程实战手册 编辑:程序博客网 时间:2024/05/01 11:24
先把例子的代码贴出来:
前端
<input type=
"button"
value=
"测试JSON数据"
onclick=
"testJSON()"
/>
<script type=
"text/javascript"
>
function
testJSON() {
$.ajax({
type:
"POST"
,
url:
"/testJson"
,
contentType:
"application/json"
,
dataType:
"json"
,
data: JSON.stringify({
"name"
:
"张三"
}),
success:
function
(jsonResult) {
alert(jsonResult);
}
});
}
</script>
后台处理代码如下:
@RequestMapping
(value =
"testJson"
)
public
String testJson(
@RequestBody
Map name, HttpServletRequest request){
System.out.println(name);
return
"jsonp"
;
}
这里需要注意的是:要在参数对象上加上@RequestBody注解,这个一定不能少,后台在接收JSON数据的时候一定要用自定义的对象或者Map对象去接收,不要用JDK中的简单对象(String/Integer/Long)来接收。
接下来我再把抓出来的http请求贴一下:
Content-Type:application/json
这里需要注意的是:Request Payload中的格式一定要和上图一致,其他格式SpringMVC会解析不出来。
OK,如上的代码就可以搞定一个JSON请求数据的解析了。下面我们来分析一下SpringMVC是怎么处理JSON请求的。
SpringMVC处理请求的简单时序图如下:
正常情况下,一个请求在SpringMVC中一般会调用doDispatch这个方法,我们进入到这个方法中直接跳到
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这一行,这一行上面的内容我们以后再找机会分析。
ha.handle这个方法会调用org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter中的handle方法,这个方法里面很简单,就是调用了handleInternal这个方法,代码如下:
public
final
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws
Exception {
return
handleInternal(request, response, (HandlerMethod) handler);
}
而handleInternal这个方法调用的是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter中的handleInternal方法,我们进入到这个方法中看看这个方法中都干了一些什么事:
@Override
protected
ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod)
throws
Exception {
ModelAndView mav;
checkRequest(request);
//检查是不是所支持的请求类型、是不是要求session
// Execute invokeHandlerMethod in synchronized block if required.
if
(
this
.synchronizeOnSession) {
//session中是不是要求同步执行
HttpSession session = request.getSession(
false
);
if
(session !=
null
) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized
(mutex) {
//同步执行方法调用
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else
{
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else
{
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
//这三个invokeHandlerMethod调用的是同一个方法
}
//缓存的设置
if
(!response.containsHeader(HEADER_CACHE_CONTROL)) {
if
(getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response,
this
.cacheSecondsForSessionAttributeHandlers);
}
else
{
prepareResponse(response);
}
}
return
mav;
}
在上面的这个方法中我们需要关注的是invokeHandlerMethod这个方法。invokeHandlerMethod这个方法有点复杂,这个方法中干了很多的事,像创建数据验证类、创建方法处理类、模型视图容器等。在这里我们先忽略这些,直接跳到
invocableMethod.invokeAndHandle(webRequest, mavContainer);
这里。这个方法在org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod中。在这个方法中我们只关注第一句话:
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
invokeForRequest这个方法在org.springframework.web.method.support.InvocableHandlerMethod中,同样在这个方法中我们也只关注第一句话:
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
getMethodArgumentValues从这个方法名我们可以看出来这个方法是获取方法参数值的,这个类和上面的方法在同一个类中。我们进到这个方法中看一下:
private
Object[] getMethodArgumentValues(NativeWebRequest request, 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);
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
//获取参数的类型(处理参数中的泛型)
args[i] = resolveProvidedArgument(parameter, providedArgs);
//如果提供了参数的值的话,直接返回
if
(args[i] !=
null
) {
continue
;
}
if
(
this
.argumentResolvers.supportsParameter(parameter)) {
//(1) 支持的参数类型
try
{
args[i] =
this
.argumentResolvers.resolveArgument(
// (2) 给参数赋值、校验的一些操作
parameter, mavContainer, request,
this
.dataBinderFactory);
continue
;
}
catch
(Exception ex) {
if
(logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage(
"Error resolving argument"
, i), ex);
}
throw
ex;
}
}
if
(args[i] ==
null
) {
String msg = getArgumentResolutionErrorMessage(
"No suitable resolver for argument"
, i);
throw
new
IllegalStateException(msg);
}
}
return
args;
}
我们先来看看上面的代码中(1)的地方。这个地方是给方法中的参数匹配一个合适的解析器。这个方法的真正调用的是
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver这个方法。
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() +
"]"
);
}
if
(methodArgumentResolver.supportsParameter(parameter)) {
//匹配合适的参数解析器并放入到缓存中
result = methodArgumentResolver;
this
.argumentResolverCache.put(parameter, result);
break
;
}
}
}
return
result;
}
那SpringMVC种提供了多少参数解析器呢?看下图所示:
大概有30来个,瞬间觉得SpringMVC好强大啊,给人一种无论你在Header里、Cookie里、Body里、还是Path里,无论是什么类型的参数我都能给你解析了的霸气。我们这里的匹配到的参数解析器是org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor这个类。我们接着来看上面代码中的(2)。resolveArgument这个方法真的调用的就是RequestResponseBodyMethodProcessor这个类中的resolveArgument的方法。我们进入到这个方法中看一下:
public
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws
Exception {
//这里是对参数的解析赋值
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
//[1]
String name = Conventions.getVariableNameForParameter(parameter);
//获取参数校验的具体类
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if
(arg !=
null
) {
validateIfApplicable(binder, parameter);
//进行参数校验
if
(binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw
new
MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return
arg;
}
我们重点看上面代码中[1]的地方。方法中的代码如下:
@Override
protected
<T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam,
Type paramType)
throws
IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
//获取请求对象
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.
class
);
ServletServerHttpRequest inputMessage =
new
ServletServerHttpRequest(servletRequest);
//从请求输入流中解析出参数的值
Object arg = readWithMessageConverters(inputMessage, methodParam, paramType);
if
(arg ==
null
) {
if
(checkRequired(methodParam)) {
//校验参数是不是必须的
throw
new
HttpMessageNotReadableException(
"Required request body is missing: "
+
methodParam.getMethod().toGenericString());
}
}
return
arg;
}
我们重点要看的是org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver中的readWithMessageConverters方法。
这个方法很长,在这个方法中会获取ContentType、参数的类型、Method、重新封装Request等等的操作。我们需要关注这三行代码:
inputMessage = getAdvice().beforeBodyRead(inputMessage, param, targetType, converterType);
body = genericConverter.read(targetType, contextClass, inputMessage);[
1
]
body = getAdvice().afterBodyRead(body, inputMessage, param, targetType, converterType);
为参数赋值的是[1]这行代码。这里调用的是org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter中的read方法,代码如下:
@Override
public
Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
throws
IOException, HttpMessageNotReadableException {
/获取Java中的类型
JavaType javaType = getJavaType(type, contextClass);
return
readJavaType(javaType, inputMessage);
//按照Java的类型,为参数赋值
}
private
Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) {
try
{
if
(inputMessage
instanceof
MappingJacksonInputMessage) {
Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
if
(deserializationView !=
null
) {
return
this
.objectMapper.readerWithView(deserializationView).forType(javaType).
readValue(inputMessage.getBody());
}
}
return
this
.objectMapper.readValue(inputMessage.getBody(), javaType);
//[1]调用Jackson中的方法,解析Body的内容,赋值为java的类型
}
catch
(IOException ex) {
throw
new
HttpMessageNotReadableException(
"Could not read document: "
+ ex.getMessage(), ex);
}
}
this.objectMapper.readValue这个方法会掉到Jackson相关的jar中。再往下跟的话还有很深,说实在的里面有很多的方法我还没看明白,所以我们就不继续往下走了。这个方法中大致干的事是按照相应的编码读取HTTP请求中请求体里的内容,由于是JSON格式的,所以又会把JSON格式的数据转换为传入进去的Java类型对象。
后记:
如果我们知道请求格式是JSON的话,我们可以自己写一个简单的请求体的解析,但是在项目中最好别这样做。代码如下:
@RequestMapping
(value =
"testJson"
)
public
String testJson(HttpServletRequest request){
try
{
InputStream inputStream = request.getInputStream();
ByteArrayOutputStream byteArrayOutputStream =
new
ByteArrayOutputStream();
byte
[] bytes =
new
byte
[
1024
];
int
flag =
0
;
while
((flag = inputStream.read(bytes)) >
0
){
byteArrayOutputStream.write(bytes,
0
,flag);
}
System.out.println(
new
String(byteArrayOutputStream.toByteArray(),request.getCharacterEncoding()));
}
catch
(IOException e) {
e.printStackTrace();
}
return
"jsonp"
;
}
请求信息如下:
后台输出结果如下:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
- SpringMVC解析JSON请求数据
- SpringMVC解析JSON请求数据小析
- Json解析网络请求数据
- SpringMVC请求json数据报406错误
- SpringMVC处理Ajax请求(请求数据为 json 格式字符串)
- springmvc之json数据的解析
- springmvc解析和输出json数据
- SpringMvc 服务端发布json数据+android客户端解析json数据
- unity3d请求json数据并解析
- [Unity3d]u3d请求json数据并解析
- IOS 请求数据解析 XML 和 JSON
- extjs前台请求json数据,并解析
- iOS 网络数据请求和Json 解析
- http请求返回并解析json数据
- unity请求json数据并解析
- URL请求并解析JSON数据
- http请求返回并解析json数据
- unity请求json数据并解析
- Qt界面设计如何让文中居中显示?
- hdu-4970-Killing Monsters(树状数组 思维)
- 循环
- hdu1892二维树状数组
- 史上最简单的 MySQL 教程(四十)「数据库变量」
- SpringMVC解析JSON请求数据
- Rsync安全配置&利用
- 强大的字符数组
- Jquery-选择器-简单的选择器
- linux -日期时间命令
- 基于 Java 的 bean 配置
- 一些Python库的安装
- Filter 过滤器
- 12.2 省选训练总结