springMVC源码分析--HttpMessageConverter写write操作(三)
上一篇博客springMVC源码分析--HttpMessageConverter参数read操作中我们已经简单介绍了参数值转换的read操作,接下来我们介绍一下返回值的处理操作。同样返回值的操作操作也是在HandlerMethodReturnValueHandler中处理的,可以参考一下老田写的springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)
简单的返回值处理示例使用@ResponseBody进行注解:
@ResponseBody
@RequestMapping("/get")
public Object get(){
Product product = new Product();
product.setDescription("hello springMVC RestFul");
product.setId(10);
product.setName("springMVC");
product.setPrice(10);
return product; //在页面中返回json数据
}
这里返回值是一个Product对象,但真正浏览器获取的数据是json数据,处理的过程就是在HttpMessageConverter中实现的 。
配置一下消息处理使用FastJSON处理的配置
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <property name="supportedMediaTypes"> <array> <value>text/html;charset=UTF-8</value> </array> </property> <property name="features"> <array> <value>WriteMapNullValue</value> <value>WriteNullStringAsEmpty</value> <value>DisableCircularReferenceDetect</value> </array> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
这样返回值的处理操作就是使用FastJsonHttpMessageConverter来进行处理,将返回值转为json数据返回。
返回值的处理是在HandlerMethodReturnValueHandlerComposite中handleReturnValue中实现的。
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
接下来是在子类RequestResponseBodyMethodProcessor的handleReturnValue中处理操作
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, webRequest);
}
在RequestResponseBodyMethodProcessor的父类AbstractMessageConverterMethodProcessor的writeWithMessageConverters中处理操作
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
接下来就是通过职责链模式选择HttpMessageConverter的实现类来进行数据转换操作。
@SuppressWarnings("unchecked")
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Class<?> returnValueClass = getReturnValueType(returnValue, returnType);
Type returnValueType = getGenericType(returnType);
HttpServletRequest servletRequest = inputMessage.getServletRequest();
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass, returnValueType);
if (returnValue != null && producibleMediaTypes.isEmpty()) {
throw new IllegalArgumentException("No converter found for return value of type: " + returnValueClass);
}
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
for (MediaType requestedType : requestedMediaTypes) {
for (MediaType producibleType : producibleMediaTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (compatibleMediaTypes.isEmpty()) {
if (returnValue != null) {
throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
}
return;
}
List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
MediaType.sortBySpecificityAndQuality(mediaTypes);
MediaType selectedMediaType = null;
for (MediaType mediaType : mediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
//匹配消息数据转换器
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
if (((GenericHttpMessageConverter<T>) messageConverter).canWrite(returnValueType,
returnValueClass, selectedMediaType)) {
returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
inputMessage, outputMessage);
if (returnValue != null) {
addContentDispositionHeader(inputMessage, outputMessage);
//进行消息转换成配置的格式
((GenericHttpMessageConverter<T>) messageConverter).write(returnValue,
returnValueType, selectedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
logger.debug("Written [" + returnValue + "] as \"" +
selectedMediaType + "\" using [" + messageConverter + "]");
}
}
return;
}
}
else if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
inputMessage, outputMessage);
if (returnValue != null) {
addContentDispositionHeader(inputMessage, outputMessage);
//将数据转换为配置的数据格式
((HttpMessageConverter<T>) messageConverter).write(returnValue,
selectedMediaType, outputMessage);
if (logger.isDebugEnabled()) {
logger.debug("Written [" + returnValue + "] as \"" +
selectedMediaType + "\" using [" + messageConverter + "]");
}
}
return;
}
}
}
if (returnValue != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
最终会选择FastJsonHttpMessageConverter 的write方法中进行处理操作,就是将数据转换为json写到输出流中
@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
HttpHeaders headers = outputMessage.getHeaders();
ByteArrayOutputStream outnew = new ByteArrayOutputStream();
int len = JSON.writeJSONString(outnew, //
fastJsonConfig.getCharset(), //
obj, //
fastJsonConfig.getSerializeConfig(), //
fastJsonConfig.getSerializeFilters(), //
fastJsonConfig.getDateFormat(), //
JSON.DEFAULT_GENERATE_FEATURE, //
fastJsonConfig.getSerializerFeatures());
headers.setContentLength(len);
OutputStream out = outputMessage.getBody();
outnew.writeTo(out);
outnew.close();
}
springMVC默认提供了很多参数和结果值处理器,包括如下:
(1)MappingJackson2HttpMessageConverter
(2)GsonHttpMessageConverter
(3)ByteArrayHttpMessageConverter
(4)ObjectToStringHttpMessageConverter
(5)ProtobufHttpMessageConverter
(6)ResourceHttpMessageConverter
(7)StringHttpMessageConverter
(8)AllEncompassingFormHttpMessageConverter
springMVC源码分析--HttpMessageConverter写write操作(三)的更多相关文章
- springMVC源码分析--HttpMessageConverter参数read操作(二)
上一篇博客springMVC源码分析--HttpMessageConverter数据转化(一)中我们简单介绍了一下HttpMessageConverter接口提供的几个方法,主要有以下几个方法: (1 ...
- springMVC源码分析--HttpMessageConverter数据转化(一)
之前的博客我们已经介绍了很多springMVC相关的模块,接下来我们介绍一下springMVC在获取参数和返回结果值方面的处理.虽然在之前的博客老田已经分别介绍了参数处理器和返回值处理器: (1)sp ...
- springMVC源码分析--拦截器HandlerExecutionChain(三)
上一篇博客springMVC源码分析--HandlerInterceptor拦截器调用过程(二)中我们介绍了HandlerInterceptor的执行调用地方,最终HandlerInterceptor ...
- springMVC源码分析--视图View(一)
之前的博客springMVC源码分析--HttpMessageConverter数据转化(一)中我们已经介绍了数据返回值的处理,在博客springMVC源码分析--ViewResolver视图解析器( ...
- springMVC源码分析--HandlerMapping(一)
HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler,其实现机制简单来说就是维持了一个url到Controller关系的Map结构,其提供的实际功能也是根据req ...
- [心得体会]SpringMVC源码分析
1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...
- springMVC源码分析--国际化实现Session和Cookie(二)
上一篇博客springMVC源码分析--国际化LocaleResolver(一)中我们介绍了springMVC提供的国际化的解决方案,接下来我们根据springMVC提供的解决方案来简单的实现一个多语 ...
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...
- springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)
之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...
随机推荐
- QT 菜单资源设置
版权声明 该文章原创于Qter开源社区(www.qter.org),作者yafeilinux,转载请注明出处! 导语 在前一篇中我们学习了使用资源文件为主窗口添加菜单图标.这次,我们 ...
- OpenStack 认证服务 KeyStone部署(三)
Keystone 介绍 Keystone作用: 用户与认证:用户权限与用户行为跟踪: 服务目录:提供一个服务目录,包括所有服务项和相关Api的断点 SOA相关知识 Keystone主要两大功能用户认证 ...
- noVNC连接CentOS,以Web方式交付VNC远程连接
什么是noVNC? noVNC 是一个 HTML5 VNC 客户端,采用 HTML 5 WebSockets, Canvas 和 JavaScript 实现,noVNC 被普遍用在各大云计算.虚拟机控 ...
- Liunx操作指令搜素引擎
链接:http://wangchujiang.com/linux-command/c/vi.html
- PHP文件引入
综述和基本语法 有4个文件加载语句:include,require,include_once,require_once. require函数通常放在 PHP 程序的最前面,PHP 程序在执行前,就会先 ...
- bzoj千题计划129:bzoj2007: [Noi2010]海拔
http://www.lydsy.com/JudgeOnline/problem.php?id=2007 1.所有点的高度一定在0~1之间, 如果有一个点的高度超过了1,那么必定会有人先上坡,再下坡, ...
- 接口测试Case之面向页面对象编写及规范
一.什么是页面对象化 主要提倡的思想是:万物皆对象,即把一个Page看成一个对象,来进行接口自动化Case的编写,不要闲扯,直接讲怎么个操作法呢? 二.有什么优势? 2.1 Case层次清晰,便于管理 ...
- zookeeper图形工具——zkui
虽然zookeeper安装包提供了客户端工具zkcli,但是命令特别少 ,每次想看看里面的节点信息特别费劲. 幸好有图形工具——zkui,https://github.com/echoma/zkui, ...
- Java开发者应该列入年度计划的5件事
本文写了我今年计划要做的5件事.为了能跟踪计划执行的进度,就把这些事都列了出来.我觉得这些事对其它Java开发者而言也是不错的参考方向. 1.开发一个应用,通过Java来操作一种NoSQL数据库实现存 ...
- web打印难题—背景不打印的简单不完美解决方案
web打印在一些开发中是比较常见的需求,最简单的办法是使用css print进行控制:对于一些建议可以参考http://slodive.com/web-development/css-print-pa ...