关于Spring @RequestBody 自动映射模型

2016年10月18日 22:17:12 稻子丶 阅读数:5049
 

在很多时候,Spring的注解为我们提供了很多方便,但只知道其用法,不懂其执行原理,有时候出错了,很难快速的定位出错原因,今天我想把自己对于@Requestbody这个注解的一点想法和大家分享下。

首先Spring处理一个请求时,请求的入口就是大家在配置文件中配置的 DispathcherServlet 这分发类,其实这个类能够接受到request的原理就是它实现了Servlet的doGet,doPost等方法,在没有正式达到Controller代码时,在它处理逻辑时,会获取Controller的反射实例,通过反射实例获取它的注解参数,执行完注解方法后,才会返回到Controller中,所以配置了@Requeat,@Valid 等注解时,返回到Controller中的都是已经经过数据绑定和校验后的对象,当Controller配置@Requestbody这个注解时,Spring会调用  AbstractMessageConverterMethodArgumentResolver 这个父类的 readWithMessageConverters 方法 通过 HttpMessageConverter类来进行解析,然后把数据要返回的对象上,再把绑定后的对象返回到Controller.

  1. protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
  2. MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
  3.  
  4. MediaType contentType;
  5. try {
  6. contentType = inputMessage.getHeaders().getContentType();
  7. }
  8. catch (InvalidMediaTypeException ex) {
  9. throw new HttpMediaTypeNotSupportedException(ex.getMessage());
  10. }
  11. if (contentType == null) {
  12. contentType = MediaType.APPLICATION_OCTET_STREAM;
  13. }
  14.  
  15. Class<?> contextClass = methodParam.getContainingClass();
  16. Class<T> targetClass = (Class<T>)
  17. ResolvableType.forMethodParameter(methodParam, targetType).resolve(Object.class);
  18.  
  19. for (HttpMessageConverter<?> converter : this.messageConverters) {
  20. if (converter instanceof GenericHttpMessageConverter) {
  21. GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
  22. if (genericConverter.canRead(targetType, contextClass, contentType)) {
  23. if (logger.isDebugEnabled()) {
  24. logger.debug("Reading [" + targetType + "] as \"" +
  25. contentType + "\" using [" + converter + "]");
  26. }
  27. return genericConverter.read(targetType, contextClass, inputMessage);
  28. }
  29. }
  30. if (converter.canRead(targetClass, contentType)) {
  31. if (logger.isDebugEnabled()) {
  32. logger.debug("Reading [" + targetClass.getName() + "] as \"" +
  33. contentType + "\" using [" + converter + "]");
  34. }
  35. return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
  36. }
  37. }
  38.  
  39. throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
  40. }

其实重要的是俩个参数,第一个是 contentType ,这个参数是从request的header中取出来的,比如你的请求header是 json ,那么这个参数的类型就是 application/json ,

第二个重要的参数就是 HttpMessageConverter 这个接口,这个接口的核心作用是,通过 contentType 判断是否request的值是否可读可写,Spring默认提供了7种messageConverters分别实现HttpMessageConverter 这个接口,我们来看下HttpMessageConverter 提供的几个接口,

  1. public interface HttpMessageConverter<T> {
  2.  
  3. /**
  4. * Indicates whether the given class can be read by this converter.
  5. * @param clazz the class to test for readability
  6. * @param mediaType the media type to read, can be {@code null} if not specified.
  7. * Typically the value of a {@code Content-Type} header.
  8. * @return {@code true} if readable; {@code false} otherwise
  9. */
  10. boolean canRead(Class<?> clazz, MediaType mediaType);
  11.  
  12. /**
  13. * Indicates whether the given class can be written by this converter.
  14. * @param clazz the class to test for writability
  15. * @param mediaType the media type to write, can be {@code null} if not specified.
  16. * Typically the value of an {@code Accept} header.
  17. * @return {@code true} if writable; {@code false} otherwise
  18. */
  19. boolean canWrite(Class<?> clazz, MediaType mediaType);
  20.  
  21. /**
  22. * Return the list of {@link MediaType} objects supported by this converter.
  23. * @return the list of supported media types
  24. */
  25. List<MediaType> getSupportedMediaTypes();
  26.  
  27. /**
  28. * Read an object of the given type form the given input message, and returns it.
  29. * @param clazz the type of object to return. This type must have previously been passed to the
  30. * {@link #canRead canRead} method of this interface, which must have returned {@code true}.
  31. * @param inputMessage the HTTP input message to read from
  32. * @return the converted object
  33. * @throws IOException in case of I/O errors
  34. * @throws HttpMessageNotReadableException in case of conversion errors
  35. */
  36. T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
  37. throws IOException, HttpMessageNotReadableException;
  38.  
  39. /**
  40. * Write an given object to the given output message.
  41. * @param t the object to write to the output message. The type of this object must have previously been
  42. * passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
  43. * @param contentType the content type to use when writing. May be {@code null} to indicate that the
  44. * default content type of the converter must be used. If not {@code null}, this media type must have
  45. * previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
  46. * returned {@code true}.
  47. * @param outputMessage the message to write to
  48. * @throws IOException in case of I/O errors
  49. * @throws HttpMessageNotWritableException in case of conversion errors
  50. */
  51. void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
  52. throws IOException, HttpMessageNotWritableException;
  53.  
  54. }

其实就是read和write的判断,分别实现这个接口的 七个类是:

1.ResourceHttpMessageConverter:负责读取资源文件和写出资源文件数据; 

2.FormHttpMessageConverter:       负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据)

3.MappingJacksonHttpMessageConverter:  负责读取和写入json格式的数据;

4.SouceHttpMessageConverter:                   负责读取和写入 xml 中javax.xml.transform.Source定义的数据;

5.Jaxb2RootElementHttpMessageConverter:  负责读取和写入xml 标签格式的数据;

6.AtomFeedHttpMessageConverter:              负责读取和写入Atom格式的数据;

7.RssChannelHttpMessageConverter:           负责读取和写入RSS格式的数据;

如果请求的contentType是json的话,那么通过循环判断可读会定位到 MappingJacksonHttpMessageConverter,其实Spring默认解析json用的是 jackson.然后会调用jackson的ObjectMapper去解析json,然后写入到要绑定的对象上。

  1. private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) {
  2. try {
  3. return this.objectMapper.readValue(inputMessage.getBody(), javaType);
  4. }
  5. catch (IOException ex) {
  6. throw new HttpMessageNotReadableException("Could not read document: " + ex.getMessage(), ex);
  7. }
  8. }

整个过程中代码其实走了挺多的,不过核心原理个人理解差不多就是这个样子,有什么不同的意见,欢迎大家指出。

关于Spring @RequestBody 自动映射模型原理的更多相关文章

  1. Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件

    本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...

  2. spring boot 自动装配的原理

    参考: https://blog.csdn.net/Dongguabai/article/details/80865599.如有侵权,请联系本人删除! 入口: import org.springfra ...

  3. Spring Boot自动配置原理与实践(一)

    前言 Spring Boot众所周知是为了简化Spring的配置,省去XML的复杂化配置(虽然Spring官方推荐也使用Java配置)采用Java+Annotation方式配置.如下几个问题是我刚开始 ...

  4. Spring Boot系列(二):Spring Boot自动装配原理解析

    一.Spring Boot整合第三方组件(Redis为例) 1.加依赖 <!--redis--> <dependency> <groupId>org.springf ...

  5. Spring Boot自动配置SpringMVC(一)

    实际上在关于Spring Boot自动配置原理实战的文章Spring Boot自动配置实战 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中,可以看到我们使用到了@ReqeusMappi ...

  6. Mybatis的模糊查询以及自动映射

    Mybatis的模糊查询 1.  参数中直接加入%% ? 1 2 3 4 5 6 7 8 9 param.setUsername("%CD%");       param.setP ...

  7. Spring Boot自动装配

    前言 一些朋友问我怎么读源码,这篇文章结合我看源码时候一些思路给大家聊聊,我主要从这三个方向出发: 确定目标,这个目标要是一个具体,不要一上来我要看懂Spring,这是不可能的,目标要这么来定,比如看 ...

  8. Spring Boot 自动配置之@Conditional的使用

    Spring Boot自动配置的"魔法"是如何实现的? 转自-https://sylvanassun.github.io/2018/01/08/2018-01-08-spring_ ...

  9. Spring Boot自动配置实战

    上篇讲述了Spring Boot自动配置的原理,本篇内容就是关于该核心原理的实际应用.需求即当某个类存在的时候,自动配置这个类的bean并且这个bean的属性可以通过application.prope ...

随机推荐

  1. jqgrid--api,官网demo,编辑

    api参考: http://blog.csdn.net/hurryjiang/article/details/7551477 官网demo: http://www.trirand.com/blog/j ...

  2. Centos6.5命令行快捷键

    ctrl+a打开一个新的终端 ctrl+l 清除屏幕内容 ctrl+a 切换到命令行开始ctrl+e 切换到命令行末尾ctrl+u 剪切光标之前的内容ctrl+k 剪切光标之后的内容 Ctrl+-&g ...

  3. windows7下c++11环境搭建

    1.安装codeblocks 13.12 2.下载安装tdm-gcc-4.8.1-3 3.配置coldblocks的编译器(settings->compiler->compiler set ...

  4. 升级MySQL 5.7版本遇到的一些小问题(转)

    在5.6版本服务器做备份 /usr/local/mysql/bin/mysqldump -S /tmp/mysql3306.sock -A -p --set-gtid-purged=OFF > ...

  5. 关于jquery登录的一些简单验证。

    获取值之后的判断 $(function () { $("#btlogin").click(function () { var txtaccount = $("#txtac ...

  6. 请问两个div之间的上下距离怎么设置

    转自:https://zhidao.baidu.com/question/344630087.html 楼上说的是一种方法,yanzilisan183 <div style="marg ...

  7. 异常:Project configuration is not up-to-date with pom.xml解决方案

    转自:https://www.cnblogs.com/zhujiabin/p/6343423.html 1. Description    Resource    Path    Location   ...

  8. 【java并发编程艺术学习】(一)初衷、感想与笔记目录

    不忘初心,方得始终. 学习java编程这么长时间,自认为在项目功能需求开发中没啥问题,但是之前的几次面试和跟一些勤奋的或者小牛.大牛级别的人的接触中,才发现自己的无知与浅薄. 学习总得有个方向吧,现阶 ...

  9. (cdh)hive 基础知识 名词详解及架构

    过程 启动 hive 之后出现的 CLI 是查询任务的入口,CLI 提交任务给 Driver Driver 接收到任务后调用 Compiler,Executor,Optimizer 将 SQL 语句转 ...

  10. shell入门-uniq去重复和tee重定向

    命令:uniq 选项:-c 显示重复数量 说明:去重复,不sort多个功能,显示几个重复 命令:tee 说明:重定向加上双重输出 [root@wangshaojun ~]# cat 2.txt1222 ...