Spring@RequestBody自動マッピングモデルについて
18780 ワード
多くの場合、Springの注釈は私たちに多くの便利さを提供してくれましたが、その使い方しか知らず、その実行原理が分からず、時には間違いがあり、迅速な位置決めが難しい原因があります.今日は@Requestbodyという注釈に対する自分の考えを皆さんと共有したいと思います.
まずSpringが要求を処理するとき、要求の入り口はみんなが配置ファイルの中で配置したDispathcherServiceletという配布クラスである.実はこのクラスがrequestを受け入れることができる原理は、ServiceletのdoGet、doPostなどの方法を実現し、正式にControllerコードに達していないとき、論理を処理するときにControllerの反射例を取得することである.その注釈パラメータを反射インスタンスで取得することにより、注釈方法を実行した後にControllerに戻るので、@Requeat,@Validなどの注釈が構成されている場合、Controllerに戻るのはデータバインドと検証済みのオブジェクトであり、Controller構成@Requestbodyという注釈の場合、SpringはA b s t r a c t M e s s a g e C o n v e r MethodArgumentResolverという親クラスのreadWithMessageConvertersメソッドを呼び出してHttpMessageConverterクラスで解析し、データを返すオブジェクト上に、バインドされたオブジェクトをControllerに返す.
実は重要なのは2つのパラメータで、1つ目はcontentTypeで、このパラメータはrequestのheaderから取り出されました.例えば、あなたのリクエストheaderがjsonであれば、このパラメータのタイプはapplication/jsonです.
2つ目の重要なパラメータはHttpMessageConverterというインタフェースです.このインタフェースの核心的な役割は、contentTypeによってrequestの値が読み書き可能かどうかを判断することです.Springはデフォルトで7種類のmessageConvertersがそれぞれHttpMessageConverterというインタフェースを実現しています.HttpMessageConverterが提供するいくつかのインタフェースを見てみましょう.
実はreadとwriteの判断で、それぞれこのインタフェースを実現する7つのクラスは:
1.ResourceHttpMessageConverter:リソースファイルの読み取りとリソースファイルデータの書き出しを担当します.
2.FormHttpMessageConverter:formが発行したデータの読み取りを担当する(読み込めるデータ形式はアプリケーション/x-www-form-urlencoded、multipart/form-data形式データの読み取りはできない)
3.MappingJacksonHttpMessageConverter:json形式のデータの読み取りと書き込みを担当する.
4.SouceHttpMessageConverter:xmlのjavaxの読み取りと書き込みを担当します.xml.transform.Source定義のデータ;
5.Jaxb 2 RootElementHttpMessageConverter:xmlラベル形式のデータの読み取りと書き込みを担当する.
6.AtomFeedHttpMessageConverter:Atom形式のデータの読み取りと書き込みを担当する.
7.RssChannelHttpMessageConverter:RSS形式のデータの読み取りと書き込みを担当する.
要求されたcontentTypeがjsonである場合、ループ判定によって読み取り可能になるとMappingJacksonHttpMessageConverterに位置付けられるが、Springデフォルト解析jsonはjacksonを用いる.次にjacksonのObjectMapperを呼び出してjsonを解析し、バインドするオブジェクトに書き込みます.
変換元:https://blog.csdn.net/geyuezhen/article/details/52853675
まずSpringが要求を処理するとき、要求の入り口はみんなが配置ファイルの中で配置したDispathcherServiceletという配布クラスである.実はこのクラスがrequestを受け入れることができる原理は、ServiceletのdoGet、doPostなどの方法を実現し、正式にControllerコードに達していないとき、論理を処理するときにControllerの反射例を取得することである.その注釈パラメータを反射インスタンスで取得することにより、注釈方法を実行した後にControllerに戻るので、@Requeat,@Validなどの注釈が構成されている場合、Controllerに戻るのはデータバインドと検証済みのオブジェクトであり、Controller構成@Requestbodyという注釈の場合、SpringはA b s t r a c t M e s s a g e C o n v e r MethodArgumentResolverという親クラスの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);
}
実は重要なのは2つのパラメータで、1つ目はcontentTypeで、このパラメータはrequestのheaderから取り出されました.例えば、あなたのリクエストheaderがjsonであれば、このパラメータのタイプはapplication/jsonです.
2つ目の重要なパラメータは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の判断で、それぞれこのインタフェースを実現する7つのクラスは:
1.ResourceHttpMessageConverter:リソースファイルの読み取りとリソースファイルデータの書き出しを担当します.
2.FormHttpMessageConverter:formが発行したデータの読み取りを担当する(読み込めるデータ形式はアプリケーション/x-www-form-urlencoded、multipart/form-data形式データの読み取りはできない)
3.MappingJacksonHttpMessageConverter:json形式のデータの読み取りと書き込みを担当する.
4.SouceHttpMessageConverter:xmlのjavaxの読み取りと書き込みを担当します.xml.transform.Source定義のデータ;
5.Jaxb 2 RootElementHttpMessageConverter: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);
}
}
変換元:https://blog.csdn.net/geyuezhen/article/details/52853675