上一篇博客springMVC源码分析--HttpMessageConverter参数read操作中我们已经简单介绍了参数值转换的read操作,接下来我们介绍一下返回值的处理操作。同样返回值的操作操作也是在HandlerMethodReturnValueHandler中处理的,可以参考一下老田写的springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)

简单的返回值处理示例使用@ResponseBody进行注解:

  1. @ResponseBody
  2. @RequestMapping("/get")
  3. public Object get(){
  4. Product product = new Product();
  5. product.setDescription("hello springMVC RestFul");
  6. product.setId(10);
  7. product.setName("springMVC");
  8. product.setPrice(10);
  9. return product; //在页面中返回json数据
  10. }

这里返回值是一个Product对象,但真正浏览器获取的数据是json数据,处理的过程就是在HttpMessageConverter中实现的 。

配置一下消息处理使用FastJSON处理的配置

  1. <mvc:annotation-driven>
  2. <mvc:message-converters register-defaults="true">
  3. <bean
  4. class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
  5. <property name="supportedMediaTypes">
  6. <array>
  7. <value>text/html;charset=UTF-8</value>
  8. </array>
  9. </property>
  10. <property name="features">
  11. <array>
  12. <value>WriteMapNullValue</value>
  13. <value>WriteNullStringAsEmpty</value>
  14. <value>DisableCircularReferenceDetect</value>
  15. </array>
  16. </property>
  17. </bean>
  18. </mvc:message-converters>
  19. </mvc:annotation-driven>

这样返回值的处理操作就是使用FastJsonHttpMessageConverter来进行处理,将返回值转为json数据返回。

返回值的处理是在HandlerMethodReturnValueHandlerComposite中handleReturnValue中实现的。

  1. @Override
  2. public void handleReturnValue(Object returnValue, MethodParameter returnType,
  3. ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  4.  
  5. HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
  6. Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
  7. handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
  8. }

接下来是在子类RequestResponseBodyMethodProcessor的handleReturnValue中处理操作

  1. @Override
  2. public void handleReturnValue(Object returnValue, MethodParameter returnType,
  3. ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
  4. throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
  5.  
  6. mavContainer.setRequestHandled(true);
  7.  
  8. // Try even with null return value. ResponseBodyAdvice could get involved.
  9. writeWithMessageConverters(returnValue, returnType, webRequest);
  10. }

在RequestResponseBodyMethodProcessor的父类AbstractMessageConverterMethodProcessor的writeWithMessageConverters中处理操作

  1. protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest)
  2. throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
  3.  
  4. ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
  5. ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
  6. writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
  7. }

接下来就是通过职责链模式选择HttpMessageConverter的实现类来进行数据转换操作。

  1. @SuppressWarnings("unchecked")
  2. protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
  3. ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
  4. throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
  5.  
  6. Class<?> returnValueClass = getReturnValueType(returnValue, returnType);
  7. Type returnValueType = getGenericType(returnType);
  8. HttpServletRequest servletRequest = inputMessage.getServletRequest();
  9. List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
  10. List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass, returnValueType);
  11.  
  12. if (returnValue != null && producibleMediaTypes.isEmpty()) {
  13. throw new IllegalArgumentException("No converter found for return value of type: " + returnValueClass);
  14. }
  15.  
  16. Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
  17. for (MediaType requestedType : requestedMediaTypes) {
  18. for (MediaType producibleType : producibleMediaTypes) {
  19. if (requestedType.isCompatibleWith(producibleType)) {
  20. compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
  21. }
  22. }
  23. }
  24. if (compatibleMediaTypes.isEmpty()) {
  25. if (returnValue != null) {
  26. throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
  27. }
  28. return;
  29. }
  30.  
  31. List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
  32. MediaType.sortBySpecificityAndQuality(mediaTypes);
  33.  
  34. MediaType selectedMediaType = null;
  35. for (MediaType mediaType : mediaTypes) {
  36. if (mediaType.isConcrete()) {
  37. selectedMediaType = mediaType;
  38. break;
  39. }
  40. else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
  41. selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
  42. break;
  43. }
  44. }
  45.  
  46. if (selectedMediaType != null) {
  47. selectedMediaType = selectedMediaType.removeQualityValue();
  48. //匹配消息数据转换器
  49. for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
  50. if (messageConverter instanceof GenericHttpMessageConverter) {
  51. if (((GenericHttpMessageConverter<T>) messageConverter).canWrite(returnValueType,
  52. returnValueClass, selectedMediaType)) {
  53. returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType,
  54. (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
  55. inputMessage, outputMessage);
  56. if (returnValue != null) {
  57. addContentDispositionHeader(inputMessage, outputMessage);
  58. //进行消息转换成配置的格式
  59. ((GenericHttpMessageConverter<T>) messageConverter).write(returnValue,
  60. returnValueType, selectedMediaType, outputMessage);
  61. if (logger.isDebugEnabled()) {
  62. logger.debug("Written [" + returnValue + "] as \"" +
  63. selectedMediaType + "\" using [" + messageConverter + "]");
  64. }
  65. }
  66. return;
  67. }
  68. }
  69. else if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
  70. returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType,
  71. (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(),
  72. inputMessage, outputMessage);
  73. if (returnValue != null) {
  74. addContentDispositionHeader(inputMessage, outputMessage);
  75. //将数据转换为配置的数据格式
  76. ((HttpMessageConverter<T>) messageConverter).write(returnValue,
  77. selectedMediaType, outputMessage);
  78. if (logger.isDebugEnabled()) {
  79. logger.debug("Written [" + returnValue + "] as \"" +
  80. selectedMediaType + "\" using [" + messageConverter + "]");
  81. }
  82. }
  83. return;
  84. }
  85. }
  86. }
  87.  
  88. if (returnValue != null) {
  89. throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
  90. }
  91. }

最终会选择FastJsonHttpMessageConverter 的write方法中进行处理操作,就是将数据转换为json写到输出流中

  1. @Override
  2. protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
  3. throws IOException, HttpMessageNotWritableException {
  4. HttpHeaders headers = outputMessage.getHeaders();
  5. ByteArrayOutputStream outnew = new ByteArrayOutputStream();
  6. int len = JSON.writeJSONString(outnew, //
  7. fastJsonConfig.getCharset(), //
  8. obj, //
  9. fastJsonConfig.getSerializeConfig(), //
  10. fastJsonConfig.getSerializeFilters(), //
  11. fastJsonConfig.getDateFormat(), //
  12. JSON.DEFAULT_GENERATE_FEATURE, //
  13. fastJsonConfig.getSerializerFeatures());
  14. headers.setContentLength(len);
  15. OutputStream out = outputMessage.getBody();
  16. outnew.writeTo(out);
  17. outnew.close();
  18. }

springMVC默认提供了很多参数和结果值处理器,包括如下:

(1)MappingJackson2HttpMessageConverter

(2)GsonHttpMessageConverter

(3)ByteArrayHttpMessageConverter

(4)ObjectToStringHttpMessageConverter

(5)ProtobufHttpMessageConverter

(6)ResourceHttpMessageConverter

(7)StringHttpMessageConverter

(8)AllEncompassingFormHttpMessageConverter

springMVC源码分析--HttpMessageConverter写write操作(三)的更多相关文章

  1. springMVC源码分析--HttpMessageConverter参数read操作(二)

    上一篇博客springMVC源码分析--HttpMessageConverter数据转化(一)中我们简单介绍了一下HttpMessageConverter接口提供的几个方法,主要有以下几个方法: (1 ...

  2. springMVC源码分析--HttpMessageConverter数据转化(一)

    之前的博客我们已经介绍了很多springMVC相关的模块,接下来我们介绍一下springMVC在获取参数和返回结果值方面的处理.虽然在之前的博客老田已经分别介绍了参数处理器和返回值处理器: (1)sp ...

  3. springMVC源码分析--拦截器HandlerExecutionChain(三)

    上一篇博客springMVC源码分析--HandlerInterceptor拦截器调用过程(二)中我们介绍了HandlerInterceptor的执行调用地方,最终HandlerInterceptor ...

  4. springMVC源码分析--视图View(一)

    之前的博客springMVC源码分析--HttpMessageConverter数据转化(一)中我们已经介绍了数据返回值的处理,在博客springMVC源码分析--ViewResolver视图解析器( ...

  5. springMVC源码分析--HandlerMapping(一)

    HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler,其实现机制简单来说就是维持了一个url到Controller关系的Map结构,其提供的实际功能也是根据req ...

  6. [心得体会]SpringMVC源码分析

    1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...

  7. springMVC源码分析--国际化实现Session和Cookie(二)

    上一篇博客springMVC源码分析--国际化LocaleResolver(一)中我们介绍了springMVC提供的国际化的解决方案,接下来我们根据springMVC提供的解决方案来简单的实现一个多语 ...

  8. SpringMVC源码分析--容器初始化(五)DispatcherServlet

    上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...

  9. springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)

    之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...

随机推荐

  1. 重启电脑后,redis 6380端口关闭重启

    zb@zb-computer:/usr/local/redis/etc$ /usr/local/redis/bin/redis-server redis.6380.conf &[1] 3062 ...

  2. P4777 【模板】扩展中国剩余定理(EXCRT)&& EXCRT

    EXCRT 不保证模数互质 \[\begin{cases} x \equiv b_1\ ({\rm mod}\ a_1) \\ x\equiv b_2\ ({\rm mod}\ a_2) \\ ... ...

  3. 简便开发插件 -- lombok

    lombok是一款可以精减java代码.提升开发人员生产效率的辅助工具,可以利用注解在编译期自动生成setter/getter/toString()/constructor代码. 彻底将开发人员从繁琐 ...

  4. bzoj千题计划123:bzoj1027: [JSOI2007]合金

    http://www.lydsy.com/JudgeOnline/problem.php?id=1027 因为x+y+z=1,所以z=1-x-y 第三维可以忽略 将x,y 看做 平面上的点 简化问题: ...

  5. Java并发编程原理与实战二十九:Exchanger

    一.简介 前面三篇博客分别介绍了CyclicBarrier.CountDownLatch.Semaphore,现在介绍并发工具类中的最后一个Exchange.Exchange是最简单的也是最复杂的,简 ...

  6. php设计模式之工厂设计模式

    概念:        工厂设计模式提供获取某个对象的新实例的一个接口,同时使调用代码避免确定实际实例化基类步骤. 很多高级模式都是依赖于工厂模式. 好处:         PHP中能够创建基于变量内容 ...

  7. lxml视频讲座

    lxml视频讲座 Winfortune 01 - How to create an equivalent of fortune and cowsay for Windows, using Python ...

  8. Linux下压缩文件-1

    tar负责打包,gzip负责压缩 tar-c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的 ...

  9. 【清华集训 2017】小Y的地铁 [模拟退火]

    小Y的地铁 Time Limit: 50 Sec  Memory Limit: 256 MB Description Input Output 对于每组输入数据,输出一行一个整数,表示除掉这 n 个换 ...

  10. spring 配置定时任务Scheduled

    一:在spring配置的xml文件添加3条命名空间 xmlns:task="http://www.springframework.org/schema/task" xsi:sche ...