1. 前面我们已经知道,解析request要找到参数解析器和返回值处理器,而对于@ResponseBody注解的方法,其实就是其对应的返回值处理器再起作用

  2. 返回值处理器,我们知道有默认15种 :

    其中处理器RequestResponseBodyMethodHandler,就是我们使用@ResponseBody时,使用的处理器,底层如下:

  3. 那么拿到对应的返回值处理器后,springMVC是如何对返回值进行操作的呢?
    debug发现,处理@ResponseBody,会调用以下方法:

    writeWithMessageConverters部分源码:

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class valueType;Object targetType;//先判断要返回的值是不是字符串类型if (value instanceof CharSequence) {body = value.toString();valueType = String.class;targetType = String.class;} else {//拿到要返回的对象body = value;//拿到要返回对对象的类型valueType = this.getReturnValueType(value, returnType);targetType = GenericTypeResolver.resolveType(this.getGenericType(returnType), returnType.getContainingClass());}//再判断返回值类型是不是资源类型(流数据),如果是,就调用下面对流数据处理的逻辑,例如直接响应if (this.isResourceType(value, returnType)) {outputMessage.getHeaders().set("Accept-Ranges", "bytes");if (value != null && inputMessage.getHeaders().getFirst("Range") != null && outputMessage.getServletResponse().getStatus() == 200) {Resource resource = (Resource)value;try {List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());body = HttpRange.toResourceRegions(httpRanges, resource);valueType = body.getClass();targetType = RESOURCE_REGION_LIST_TYPE;} catch (IllegalArgumentException var19) {outputMessage.getHeaders().set("Content-Range", "bytes */" + resource.contentLength());outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());}}}//(媒体类型)内容协商,浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)//服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据MediaType selectedMediaType = null;MediaType contentType = outputMessage.getHeaders().getContentType();boolean isContentTypePreset = contentType != null && contentType.isConcrete();if (isContentTypePreset) {if (this.logger.isDebugEnabled()) {this.logger.debug("Found 'Content-Type:" + contentType + "' in response");}selectedMediaType = contentType;} else {HttpServletRequest request = inputMessage.getServletRequest();List<MediaType> acceptableTypes = this.getAcceptableMediaTypes(request);List<MediaType> producibleTypes = this.getProducibleMediaTypes(request, valueType, (Type)targetType);if (body != null && producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);}List<MediaType> mediaTypesToUse = new ArrayList();Iterator var15 = acceptableTypes.iterator();MediaType mediaType;while(var15.hasNext()) {mediaType = (MediaType)var15.next();Iterator var17 = producibleTypes.iterator();while(var17.hasNext()) {MediaType producibleType = (MediaType)var17.next();if (mediaType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(this.getMostSpecificMediaType(mediaType, producibleType));}}}if (mediaTypesToUse.isEmpty()) {if (body != null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);}if (this.logger.isDebugEnabled()) {this.logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);}return;}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);var15 = mediaTypesToUse.iterator();//遍历协商,得到可以返回的数据类型while(var15.hasNext()) {mediaType = (MediaType)var15.next();if (mediaType.isConcrete()) {selectedMediaType = mediaType;break;}if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;break;}}if (this.logger.isDebugEnabled()) {this.logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes);}}HttpMessageConverter converter;GenericHttpMessageConverter genericConverter;label159: {if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();Iterator var22 = this.messageConverters.iterator();//遍历所有的HttpMessageConverter(看是否支持将 此 Class类型的对象,转为MediaType类型的数据,过程是可逆的)//得到可匹配的消息转换器,支持将对象转成Json数据,这就是关键的核心//最终得到MappingJackson2HttpMessageConverter可以将对象写为jsonwhile(var22.hasNext()) {converter = (HttpMessageConverter)var22.next();genericConverter = converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter)converter : null;if (genericConverter != null) {if (((GenericHttpMessageConverter)converter).canWrite((Type)targetType, valueType, selectedMediaType)) {break label159;}} else if (converter.canWrite(valueType, selectedMediaType)) {break label159;}}}
}
  • 最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的

  • MessageConverter规范

  • 默认的MessageConverter

0 - 只支持Byte类型的
1 - String
2 - String
3 - Resource
4 - ResourceRegion
5 - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
6 - MultiValueMap
7 - true
8 - true
9 - 支持注解方式xml处理的。
  • 总结:
• 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
• 2、返回值处理器调用 handleReturnValue 进行处理
• 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。• 利用 MessageConverters 进行处理 将数据写为json• A.内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)• B.服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,• C.SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?• a.得到MappingJackson2HttpMessageConverter可以将对象写为json• b.利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
  • 附加:文件返回方式
    FileSystemResource 是Resource的实现类,所以springMVC最终会调用对应的messageConverter进行处理

SpringBoot对于标注@ResponseBody注解返回JSON数据的处理相关推荐

  1. SpringBoot项目解决@ResponseBody注解返回xml格式数据而不是json格式的问题

    问题: 一般情况下,@RestController中的接口默认响应数据格式都是 json 格式的数据,但有时候使用某些依赖包,会影响@ResponseBody的响应数据类型为xml格式,问题重现如下: ...

  2. Java @responsebody,springMVC 使用注解@ResponseBody 不能返回JSON数据

    控制器中代码 @RequestMapping(value = "/listArea",method = RequestMethod.GET) @ResponseBody priva ...

  3. spring mvc 返回html 乱码,解决springmvc使用ResponseBody注解返回json中文乱码问题

    spring版本:4.2.5.RELEASE 查看"org.springframework.http.converter.StringHttpMessageConverter"源码 ...

  4. springboot shiro ajax,SpringBoot Shiro 登录成功后返回json数据 shiro使用ajax登录

    老规矩,先上代码: protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletReques ...

  5. 从零开始学springboot笔记(二)-Spring boot返回json数据(中文无乱码)

    先创建json实体类,如下: public class Demo {private int age; private String address; private String name; priv ...

  6. 解决@ResponseBody注解返回的json中文乱码问题

    解决@ResponseBody注解返回的json中文乱码问题 参考文章: (1)解决@ResponseBody注解返回的json中文乱码问题 (2)https://www.cnblogs.com/hu ...

  7. @ResponseBody返回JSON数据,360安全浏览器弹出下载页面

    文章目录 问题重现 解决方法 成功解决 问题重现 Controller中使用@ResponseBody返回JSON数据. @Controller public class StudentControl ...

  8. SpringMVC响应使用案例(带数据页面跳转,快捷访问路径,返回json数据)

    页面跳转 转发(默认) @RequestMapping("/showPage1") public String showPage1() {System.out.println(&q ...

  9. spring MVC之返回JSON数据(Spring3.0 MVC+Jackson+AJAX)

    参考: http://angelbill3.iteye.com/blog/1985075 问题:在进行springmvc返回json数据的时候报如下错误:用上面的controller,访问:http: ...

最新文章

  1. 行走智慧城市 数据要有统一“身份”
  2. (转)iOS7界面设计规范(1) - UI基础 - 为iOS7而设计
  3. SAP UI5 应用开发教程之二十四 - 如何使用 OData 数据模型
  4. Python机器学习---2.聚类算法理论部分
  5. Java的最新发展– 2018年4月下旬
  6. 前端学习(2559):双向数据和单向数据不冲突
  7. 9008刷机模式写入超时刷机帮_刷机时没有成功,然后变成黑砖,usb接口直接变成未知设备~希望大神救助!...
  8. java shp文件_Java读取工作空间下所有shp文件名
  9. 强上阿里云之安装MYSQL
  10. Spark报错:JDOFatalInternalException: Error creating transactional connection factory
  11. iPhone手机更换自定义铃声
  12. angular 上传图像的使用总结
  13. python流程自动化_python selenium 自动化流程的一些总结与思考
  14. 定时器函数执行原理揭秘
  15. vue使用ntko控件完成word上传、html上传
  16. 知识图谱:【图数据库Nebula(一)】——Nebula简介
  17. 【Django】基于PythonWeb的Django框架设计实现天天生鲜系统-5数据库操作
  18. LightOJ 1071 Baker Vai(记忆化搜索)
  19. 《东周列国志》第八十五回 乐羊子怒啜中山羹 西门豹乔送河伯妇
  20. Spring Boot 错误页面解析原理(超级无敌详细)

热门文章

  1. 去掉PE文件随机基址的方法
  2. 机器学习——正则化-L2
  3. Web开发环境搭建 Eclipse-Java EE 篇
  4. 5.intent_activity
  5. BZOJ1555 KD之死
  6. Sublime Text 3 中文乱码的解决方法
  7. Android Studio 生成签名的APK
  8. sparse double型矩阵转为full矩阵
  9. 【Python】判断字符串 str 是否为空
  10. 【Leetcode】那些年四数之和下的评论