SpringMVC restful 注解之@RequestBody进行json与object转换


Posted in Javascript onDecember 10, 2015

由于快过年的原因,项目组没有太多任务,闲来无事研究了一下spring中restful调用。发现spring竟然已经强大到如此境界,程序员已经不需要在关心在写接口的过程中数据的转换以及调用,只需要专注业务。下面我总结一下步骤及其在研究过程的遇到的问题。

步骤:

           1、git clone https://github.com/spring-guides/gs-rest-service.git 从spring官网上下载了源码

           2、进行maven编译(gradle也行)

           3、运行、访问http://localhost:8080/greeting

           4、运行结果能把对象转换为json对象返回给页面

 这时我就在思考怎样能让请求的数据自动转换为java对象呢,通过google,发现其实spring已经提供了HttpMessageConverter转换器,而且默认情况下是加载了 MappingJackson2HttpMessageConverter(json ~object转换的类)。只需要配置@RequestBody Greeting gree 即可使用。

controller层代码如下:

@RequestMapping(value = "/greeting", method = RequestMethod.POST,consumes = "application/json")
  public @ResponseBody Greeting greeting(@RequestBody Greeting gree) { 
    System.out.println(gree.getContent());
    return gree;
  }

这时候我通过谷歌的插件(postman)进行调用,死活调用不成功!

分析问题及解决问题:

这时我感觉问题的原因可能出在如下几个方面:

 1、spring默认没有加载MappingJackson2HttpMessageConverter(不知道具体加载方式)

 2、MappingJackson2HttpMessageConverter加载后不能工作(不知道不工作原因)

其实最后面导致不工作的原因是太相信spring的源码(对象没有提供set方法导致),带着这两疑问在网上海量搜索者找不到对应结果。没有办法只能从根本上找到问题原因,看spring源代码。

针对第一个问题:

    第一步:手动重写加载类型转换器

@Configuration
  @EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
  public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    System.out.println("init convert is start !!!!!");
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
    stringConverter.setWriteAcceptCharset(false);
    messageConverters.add(new MappingJackson2HttpMessageConverter());
    System.out.println("init convert is stop !!!!!");
  }
}

测试发现还是不能使用,这时就更不清楚原因了。只能看默认情况下spring是怎么加载类型转换器的。结果发现在WebMvcConfigurationSupport中这个方法addDefaultHttpMessageConverters(HttpMessageConverter这个关键字反射搜索到使用地方通过判断及其跟踪找到的)中如下代码:

@SuppressWarnings("deprecation")
  protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
    stringConverter.setWriteAcceptCharset(false);
    messageConverters.add(new ByteArrayHttpMessageConverter());
    messageConverters.add(stringConverter);
    messageConverters.add(new ResourceHttpMessageConverter());
    messageConverters.add(new SourceHttpMessageConverter<Source>());
    messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    if (romePresent) {
      messageConverters.add(new AtomFeedHttpMessageConverter());
      messageConverters.add(new RssChannelHttpMessageConverter());
    }
    if (jaxb2Present) {
      messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }
    if (jackson2Present) {
      messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    else if (jacksonPresent) {
      messageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter());
    }
  }

已经加载了相应的默认转换器。断点调试说明默认配置是没有问题的。

只能说明是第二个问题导致的,但是不知道为什么导致这个问题(json数据问题,还是其他问题),在不知道问题的情况下,只能看request请求过来,转换器是怎么工作的。因为本人对spring不是特别了解,所以不知其原理。在这种情况下还是只能根据(HttpMessageConverter)关键类找到相应使用地方。以经验进行判断和调试。发现在AbstractMessageConverterMethodArgumentResolver中的readWithMessageConverters方法是request请求过来进行类型转换的处理方法。

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
      MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
    MediaType contentType;
    try {
      contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
      throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    if (contentType == null) {
      contentType = MediaType.APPLICATION_OCTET_STREAM;
    }
    Class<?> contextClass = methodParam.getContainingClass();
    Class<T> targetClass = (Class<T>) ResolvableType.forType(targetType,
        ResolvableType.forMethodParameter(methodParam)).resolve();
    for (HttpMessageConverter<?> converter : this.messageConverters) {
      if (converter instanceof GenericHttpMessageConverter) {
        GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
        if (genericConverter.canRead(targetType, contextClass, contentType)) {
          if (logger.isDebugEnabled()) {
            logger.debug("Reading [" + targetType + "] as \"" +
                contentType + "\" using [" + converter + "]");
          }
          return genericConverter.read(targetType, contextClass, inputMessage);
        }
      }
      if (targetClass != null) {
        if (converter.canRead(targetClass, contentType)) {
          if (logger.isDebugEnabled()) {
            logger.debug("Reading [" + targetClass.getName() + "] as \"" +
                contentType + "\" using [" + converter + "]");
          }
          return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
        }
      }
    }
    throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
  }

这时候发现其实已经根据HttpMessageConverter的canRead方法已经找到了对应的类型消息转换器MappingJackson2HttpMessageConverter,而且已经开始进行转换了,只是抛出了运行时异常。因为异常没有在控制台输出。我通过断点调试发现MappingJackson2HttpMessageConverter的readJavaType方法抛出运行时异常,通过源代码发现底层是用的jackson的objectMapper进行操作的,代码如下:

try {
      return this.objectMapper.readValue(inputMessage.getBody(), javaType);
    }
    catch (IOException ex) {
      throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
    }

如是我就把代码单独拿出来在main方法里面运行,还是不行,这时我就好定位问题了。要不是类型错误,要不是输入数据错误。仔细检查发现json数据没有问题,用jsonobject也能进行转换。这时只能判断是传入的javaType有问题导致的。如是我打开发现对象(Greeting)没有set方法,我想是不是因为此jakson没法工作呢(原理不清楚)。如是乎我给此对象提供了set方法,再运行可以了。绕了一圈终于把问题解决了,但是通过这个问题让我更加清楚了spring的restful的工作机制。

Javascript 相关文章推荐
使用Jquery实现每日签到功能
Apr 03 Javascript
javascript实现一个数值加法函数
Jun 26 Javascript
百度地图api如何使用
Aug 03 Javascript
详解Node.Js如何处理post数据
Sep 19 Javascript
AngularJS通过$sce输出html的方法
Sep 22 Javascript
vue中的provide/inject的学习使用
May 09 Javascript
Vue 获取数组键名的方法
Jun 21 Javascript
详解处理Vue单页面应用SEO的另一种思路
Nov 09 Javascript
Vue项目中使用jquery的简单方法
May 16 jQuery
VUE 组件转换为微信小程序组件的方法
Nov 06 Javascript
聊聊Vue中provide/inject的应用详解
Nov 10 Javascript
vue 获取url参数、get参数返回数组的操作
Nov 12 Javascript
Spring mvc 接收json对象
Dec 10 #Javascript
SpringMVC返回json数据的三种方式
Dec 10 #Javascript
js操作数组函数实例小结
Dec 10 #Javascript
SpringMVC框架下JQuery传递并解析Json格式的数据是如何实现的
Dec 10 #Javascript
jQuery自定义动画函数实例详解(附demo源码)
Dec 10 #Javascript
javascript图片预加载完整实例
Dec 10 #Javascript
JavaScript动态插入CSS的方法
Dec 10 #Javascript
You might like
写一个用户在线显示的程序
2006/10/09 PHP
使用 MySQL 开始 PHP 会话
2006/12/21 PHP
最令PHP初学者们头痛的十四个问题
2007/01/15 PHP
js阻止冒泡及jquery阻止事件冒泡示例介绍
2013/11/19 Javascript
浅析document.ready和window.onload的区别讲解
2013/12/18 Javascript
jQuery新的事件绑定机制on()示例应用
2014/07/18 Javascript
浅谈jQuery.easyui的datebox格式化时间
2015/06/25 Javascript
jQuery实现布局高宽自适应的简单实例
2016/05/28 Javascript
AngularJS通过$sce输出html的方法
2016/09/22 Javascript
js给table赋值的实例代码
2016/10/13 Javascript
jQuery实现ajax无刷新分页页码控件
2017/02/28 Javascript
详解webpack2+React 实例demo
2017/09/11 Javascript
Vue2.0子同级组件之间数据交互方法
2018/02/28 Javascript
js异步上传多张图片插件的使用方法
2018/10/22 Javascript
Vue项目中使用better-scroll实现一个轮播图自动播放功能
2018/12/03 Javascript
Vue2.0 ES6语法降级ES5的操作
2020/10/30 Javascript
mustache.js实现首页元件动态渲染的示例代码
2020/12/28 Javascript
vue穿梭框实现上下移动
2021/01/29 Vue.js
python实现定时播放mp3
2015/03/29 Python
python链接Oracle数据库的方法
2015/06/28 Python
Python+OpenCV 实现图片无损旋转90°且无黑边
2019/12/12 Python
Python基于tkinter canvas实现图片裁剪功能
2020/11/05 Python
css3实现针线缝合效果(图解步骤)
2013/02/04 HTML / CSS
BannerBuzz加拿大:在线定制横幅印刷、广告和标志
2020/03/10 全球购物
汽车检测与维修专业求职信
2013/10/30 职场文书
美术毕业生求职信
2014/02/25 职场文书
数学高效课堂实施方案
2014/03/29 职场文书
《三顾茅庐》教学反思
2014/04/10 职场文书
《翻越远方的大山》教学反思
2014/04/13 职场文书
陈安之励志演讲稿
2014/08/21 职场文书
2014小学数学教研组工作总结
2014/12/06 职场文书
优秀班主任先进事迹材料
2014/12/16 职场文书
2015年学生会纪检部工作总结
2015/03/31 职场文书
Html5同时支持多端sdk的小技巧
2021/11/17 HTML / CSS
动态规划之使用备忘录来改进Javascript函数
2022/04/07 Javascript
vue如何实现关闭对话框后刷新列表
2022/04/08 Vue.js