关于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.

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.forMethodParameter(methodParam, targetType).resolve(Object.class); 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 (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, this.allSupportedMediaTypes);
}

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

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

public interface HttpMessageConverter<T> {

   /**
* Indicates whether the given class can be read by this converter.
* @param clazz the class to test for readability
* @param mediaType the media type to read, can be {@code null} if not specified.
* Typically the value of a {@code Content-Type} header.
* @return {@code true} if readable; {@code false} otherwise
*/
boolean canRead(Class<?> clazz, MediaType mediaType); /**
* Indicates whether the given class can be written by this converter.
* @param clazz the class to test for writability
* @param mediaType the media type to write, can be {@code null} if not specified.
* Typically the value of an {@code Accept} header.
* @return {@code true} if writable; {@code false} otherwise
*/
boolean canWrite(Class<?> clazz, MediaType mediaType); /**
* Return the list of {@link MediaType} objects supported by this converter.
* @return the list of supported media types
*/
List<MediaType> getSupportedMediaTypes(); /**
* Read an object of the given type form the given input message, and returns it.
* @param clazz the type of object to return. This type must have previously been passed to the
* {@link #canRead canRead} method of this interface, which must have returned {@code true}.
* @param inputMessage the HTTP input message to read from
* @return the converted object
* @throws IOException in case of I/O errors
* @throws HttpMessageNotReadableException in case of conversion errors
*/
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException; /**
* Write an given object to the given output message.
* @param t the object to write to the output message. The type of this object must have previously been
* passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
* @param contentType the content type to use when writing. May be {@code null} to indicate that the
* default content type of the converter must be used. If not {@code null}, this media type must have
* previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
* returned {@code true}.
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException; }

其实就是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,然后写入到要绑定的对象上。

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

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

关于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. android开发之数据库存取图片

    Android数据库中存取图片通常使用两种方式,一种是保存图片所在路径,二是将图片以二进制的形式存储(sqlite3支持BLOB数据类型).对于两种方法的使用,好像第二种方法不如第一种方法更受程序员欢 ...

  2. python setuptools安装

    执行# python setup.py install 时发生如下错误 Traceback (most recent call last): File "setup.py", li ...

  3. 江苏省大学生程序设计竞赛(JSCPC)赛后感

    仔细的算了一下,这是我第6次参加与acm有关的比赛了,每一次的经历,在我看来都是一次对自己能力的认识与评估,身边学计算机专业的同龄人对这项比赛的热爱,专注,勇气以及所获得的成就让要好好努力,更要加倍付 ...

  4. Guice 学习

    Guice: 是一个轻量级的DI框架. 不需要繁琐的配置,只需要定义一个Module来表述接口和实现类,以及父类和子类之间的关联关系的绑定,如下是一个例子. http://blog.csdn.net/ ...

  5. 51单片机的TXD、 RXD 既接了 232 又接了 485芯片 ,会导致通信失败!

    51单片机的TXD. RXD 既接了 232 又接了 485 ,会导致通信失败! 下面是绘制电路板用的部分电路图: 通信现象: 1.我使用了USB-232的下载模块,把它接到P4上,发现单片机只能发送 ...

  6. 10_android打包的过程

    java代码先编译成.class,最后打包成.dex.resources  uncompiled resources:不需要编译的资源:资产目录assets 清单文件 用来标识唯一的安卓应用:签名和包 ...

  7. 【总结整理】http-https

    上面这种加密消息的方式就是对称加密,你知道如何加密,也知道如何解码.然后李雷跟韩梅梅用的字母表偏移的加密方法叫 Caesar cipher, 凯撒加密.现实世界中用的加密算法会更复杂,但是基本原理相同 ...

  8. nodejs中 underscore 包有什么作用

    Underscore一个JavaScript实用库,提供了一整套函数式变成有用的实用功能,但是没有扩展任何JavaScript内置对象.它是这个问题的答案:“如果我在一个空白的HTML页面前坐下, 并 ...

  9. Flask01 初识flask、创建flask应用、flask启动配置

    1 什么是flask Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 . 百度百科:点击前往 中文文档: ...

  10. 6.7 root和user之间的切换