给接口写假数据的时候,为了返回到前台是一个JSON串,所以加了@ResponseBody注解.但是这个注解我以前一直没有用过.所以和@RequestBody一起了解下.
@RequestBody,这个注解用于读取Request请求的body部分数据,使用系统默认的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;然后再把HttpMessageConverter返回的对象数据绑定到controller中方法的参数上.
若是GET,POST方式提交时,根据request的header中的Content-Type的值来判断(说明一下,request请求中的body部分的数据编码格式是由header部分的Content-Type制定的.).
如果是application/x-www-form-urlencoded,则是否使用@RequestBody时为可选,也可以用@RequestParam,@ModelAttribute来处理.若是multipart/form-data,@RequestBody不能处理这种格式的数据;而其他的格式,必须使用@RequestBody来处理,包括application/json,application/xml.
若是PUT方式提交的,也是根据Content-Type的值来判断.application/x-www-form-urlencoded这回是必须用@RequestBody来处理,而multipart/form-data,则同样还是不能处理;其他,也同样还是必须用@RequestBody来处理.
而@ResponseBody,则是用于将controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区.通常在返回的数据不是html标签的页面,而是其他某个格式的数据(如json,xml等)时使用.
查看HttpMessageConvert的源码.
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, MediaType mediaType);
boolean canWrite(Class<?> clazz, MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
这是一个接口,该接口中定义了5个方法,除了getSupportedMediaTypes方法,其他4个分别是读取数据时的canRead(),read()和写入数据时的canWrite(),write()方法.
那HttpMessageConvert是在什么时候配置的?
在使用<MVC:annotation-driven />注解驱动标签配置时,默认配置了RequestMappingHandlerAdapter,并为他配置了一下默认的HttpMessageConverter. 如下是提供的九种HttpMessageConverter消息转换器.
ByteArrayHttpMessageConverter converts byte arrays.
StringHttpMessageConverter converts strings.
ResourceHttpMessageConverter converts to/from org.springframework.core.io.Resource for all media types.
SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>.
Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added if JAXB2 is present on the classpath.
MappingJacksonHttpMessageConverter converts to/from JSON — added if Jackson is present on the classpath.
AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on the classpath.
RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present on the classpath.
ByteArrayHttpMessageConverter:负责转换二进制数组格式的数据的转换.包括读取二进制格式的数据和写出二进制格式的数据;
StringHttpMessageConverter,用于读写字符串格式的数据;
ResourceHttpMessageConverter,用于读写资源文件;
SourceHttpMessageConverter,用于读写xml中的javax.xml.transform.Source定义的数据;
FormHttpMessageConverter,用于读form提交的数据.能读取application/x-www-form-urlencoded,不能读取multipart/form-data格式数据;以及写入application/x-www-form-urlencoded和multipart/form-data格式的数据;
Jaxb2RootElementHttpMessageConverter,用于读写xml标签格式的数据;
MappingJacksonHttpMessageConverter,用于读写json格式的数据;
AtomFeedHttpMessageConverter,用于读写Atom格式的数据;
RssChannelHttpMessageConverter,用于读写Rss格式的数据.
当我们使用@RequestBody或@ResponseBody注解时,RequestMappingHandlerAdapter请求对应的适配器就根据情况,使用上述的9种转换器来读取或者写入相应格式的数据.
而HttpMessageConverter匹配的过程,对于@RequestBody注解,就是根据Request对象的Content-Type类型,来逐一匹配合适的HttpMessageConverter,然后读取Request中的body数据.
spring 3.1源代码如下:
private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
throws Exception {
MediaType contentType = inputMessage.getHeaders().getContentType();
if (contentType == null) {
StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
String paramName = methodParam.getParameterName();
if (paramName != null) {
builder.append(' ');
builder.append(paramName);
}
throw new HttpMediaTypeNotSupportedException(
"Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
}
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
if (this.messageConverters != null) {
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
if (messageConverter.canRead(paramType, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType
+"\" using [" + messageConverter + "]");
}
return messageConverter.read(paramType, inputMessage);
}
}
}
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
}
方法为readWithMessageConverter,用消息转换器读,参数为MethodParamter方法的参数,HttpInputMessage,输入消息,以及paramType,参数的class类型.并抛出Exception异常.
首先获取到输入参数中的header部分的Content-Type,这是一个MediaType类型的值.若该值为null,则抛异常.若有值,则继续执行.遍历所有的messageConverters,若根据参数类型和content-type能够读取,则读取输入消息中的内容.即匹配成功.
而@ResponseBody注解时,则时根据Request对象的header部分的Accept属性(逗号分隔),逐一按accept中的类型,去遍历能处理的HttpMessageConverter.
private void writeWithMessageConverters(Object returnValue,
HttpInputMessage inputMessage, HttpOutputMessage outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException {
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
if (acceptedMediaTypes.isEmpty()) {
acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
}
MediaType.sortByQualityValue(acceptedMediaTypes);
Class<?> returnValueType = returnValue.getClass();
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
if (getMessageConverters() != null) {
for (MediaType acceptedMediaType : acceptedMediaTypes) {
for (HttpMessageConverter messageConverter : getMessageConverters()) {
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
messageConverter.write(returnValue, acceptedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = acceptedMediaType;
}
logger.debug("Written [" + returnValue + "] as \"" + contentType +
"\" using [" + messageConverter + "]");
}
this.responseArgumentUsed = true;
return;
}
}
}
for (HttpMessageConverter messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
}
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
}
wirteWithMessageConverters,用MessageConverters写入,参数为Object类型的returnValue,HttpInputMessage类型的输入消息,以及HttpOutputMessage的输出消息,抛出IO异常,Media类型不被接受异常.
首先获取输入消息的header的accept,这是一个MediaType类型的list集合.然后对该list集合进行排序.然后遍历list集合,并遍历所有的MessageConverter,若当前的MessageConverter可以配合returnValue的类型,写入集合中的当前记录,并写入到输出消息中,则匹配成功.
其他,MappingJacksonHttpMessageConverter调用了objectMapper.writeValue(OutputStream stream,Object obj)方法,使用@ResponseBody注解返回的对象就传入到Object参数内.若返回的对象为已经格式化的json串时,不使用@ResponseBody注解,而应该这样处理:
1.response.setContentType("application/json;charset=UTF-8");
2.response.getWriter().print(jsonSr);
直接输出到body去,然后就不需要返回String字符串作为视图了,返回值为void.