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 相关文章推荐
JavaScript的内存释放问题详解
Jan 21 Javascript
js控制div弹出层实现方法
May 11 Javascript
Jquery操作cookie记住用户名
Mar 29 Javascript
js从外部获取图片的实现方法
Aug 05 Javascript
文件上传,iframe跨域数据提交的实现
Nov 18 Javascript
兼容浏览器的js事件绑定函数(详解)
May 09 Javascript
JS实现图片手风琴效果
Apr 17 Javascript
jQuery动态添加li标签并添加属性和绑定事件方法
Feb 24 jQuery
微信小程序使用wxParse解析html的实现示例
Aug 30 Javascript
vue中使用vue-print.js实现多页打印
Mar 05 Javascript
解决vue下载后台传过来的乱码流的问题
Dec 05 Vue.js
JS实现刷新网页后之前浏览位置保持不变示例详解
Aug 14 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
PHP 数组遍历方法大全(foreach,list,each)
2010/06/30 PHP
Ping服务的php实现方法,让网站快速被收录
2012/02/04 PHP
php的mkdir()函数创建文件夹比较安全的权限设置方法
2014/07/28 PHP
php模拟post提交数据的方法
2015/02/12 PHP
Symfony2安装的方法(2种方法)
2016/02/04 PHP
php常用数组array函数实例总结【赋值,拆分,合并,计算,添加,删除,查询,判断,排序】
2016/12/07 PHP
php获取ajax的headers方法与内容实例
2017/12/27 PHP
Javascript生成json的函数代码(可以用php的json_decode解码)
2012/06/11 Javascript
重写javascript中window.confirm的行为
2012/10/21 Javascript
javascript和HTML5利用canvas构建猜牌游戏实现算法
2013/07/17 Javascript
bootstrap-datetimepicker实现只显示到日期的方法
2016/11/25 Javascript
微信小程序 两种滑动方式(横向滑动,竖向滑动)详细及实例代码
2017/01/13 Javascript
canvas的神奇用法
2017/02/03 Javascript
javascript数组去重常用方法实例分析
2017/04/11 Javascript
利用JavaScript如何查询某个值是否数组内
2017/07/30 Javascript
vue.js $refs和$emit 父子组件交互的方法
2017/12/20 Javascript
微信小程序实现手指拖动选项排序
2020/04/22 Javascript
[06:45]DOTA2-DPC中国联赛 正赛 Magma vs LBZS 选手采访
2021/03/11 DOTA
python文件读写并使用mysql批量插入示例分享(python操作mysql)
2014/02/17 Python
python判断windows隐藏文件的方法
2014/03/21 Python
python类继承与子类实例初始化用法分析
2015/04/17 Python
python调用百度地图WEB服务API获取地点对应坐标值
2019/01/16 Python
3种python调用其他脚本的方法
2020/01/06 Python
Python3加密解密库Crypto的RSA加解密和签名/验签实现方法实例
2020/02/11 Python
pytorch Dataset,DataLoader产生自定义的训练数据案例
2021/03/03 Python
Roxy美国官网:澳大利亚冲浪、滑雪健身品牌
2016/07/30 全球购物
建筑设计所实习生自我鉴定
2013/09/25 职场文书
实习期自我鉴定
2013/10/11 职场文书
心理健康课教学反思
2014/02/13 职场文书
英语求职信范文
2014/05/23 职场文书
旅游与酒店管理专业求职信
2014/07/21 职场文书
2015年音乐教师个人工作总结
2015/05/20 职场文书
四年级语文教学反思
2016/03/03 职场文书
Mysql中 unique列插入重复值该怎么解决呢
2021/05/26 MySQL
Spring mvc是如何实现与数据库的前后端的连接操作的?
2021/06/30 Java/Android
Java反应式框架Reactor中的Mono和Flux
2021/07/25 Java/Android