热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringMVC关于json、xml自动转换的原理研究[附带源码分析]

目录前言现象源码分析实例讲解关于配置总结参考资料前言SpringMVC是目前主流的WebMVC框架之一。如果有同学对它不熟悉,那么请参考它的入门blog:http:www.cnblogs.co

目录

前言

SpringMVC是目前主流的Web MVC框架之一。 

如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html

现象

本文使用的demo基于maven,是根据入门blog的例子继续写下去的。

我们先来看一看对应的现象。 我们这里的配置文件 *-dispatcher.xml中的关键配置如下(其他常规的配置文件不在讲解,可参考本文一开始提到的入门blog):

(视图配置省略)

<mvc:resources location="/static/" mapping="/static/**"/>
<mvc:annotation-driven/>
<context:component-scan base-package="org.format.demo.controller"/>

pom中需要有以下依赖(Spring依赖及其他依赖不显示):

<dependency>
<groupId>org.codehaus.jacksongroupId>
<artifactId>jackson-core-aslartifactId>
<version>1.9.13version>
dependency>
<dependency>
<groupId>org.codehaus.jacksongroupId>
<artifactId>jackson-mapper-aslartifactId>
<version>1.9.13version>
dependency>

这个依赖是json序列化的依赖。

ok。我们在Controller中添加一个method:

@RequestMapping("/xmlOrJson")
@ResponseBody
public Map xmlOrJson() {
Map map = new HashMap();
map.put("list", employeeService.list());
return map;
}

直接访问地址:

我们看到,短短几行配置。使用@ResponseBody注解之后,Controller返回的对象 自动被转换成对应的json数据,在这里不得不感叹SpringMVC的强大。

我们好像也没看到具体的配置,唯一看到的就是*-dispatcher.xml中的一句配置:。其实就是这个配置,导致了java对象自动转换成json对象的现象。

那么spring到底是如何实现java对象到json对象的自动转换的呢? 为什么转换成了json数据,如果想转换成xml数据,那该怎么办?

源码分析

本文使用的spring版本是4.0.2。  

在讲解这个配置之前,我们先了解下Spring的消息转换机制。@ResponseBody这个注解就是使用消息转换机制,最终通过json的转换器转换成json数据的。

HttpMessageConverter接口就是Spring提供的http消息转换接口。有关这方面的知识大家可以参考"参考资料"中的第二条链接,里面讲的很清楚。

下面开始分析这句配置:

这句代码在spring中的解析类是:

在AnnotationDrivenBeanDefinitionParser源码的152行parse方法中:

分别实例化了RequestMappingHandlerMapping,ConfigurableWebBindingInitializer,RequestMappingHandlerAdapter等诸多类。

其中RequestMappingHandlerMapping和RequestMappingHandlerAdapter这两个类比较重要。

RequestMappingHandlerMapping处理请求映射的,处理@RequestMapping跟请求地址之间的关系。

RequestMappingHandlerAdapter是请求处理的适配器,也就是请求之后处理具体逻辑的执行,关系到哪个类的哪个方法以及转换器等工作,这个类是我们讲的重点,其中它的属性messageConverters是本文要讲的重点。

私有方法:getMessageConverters

从代码中我们可以,RequestMappingHandlerAdapter设置messageConverters的逻辑:

1.如果节点有子节点message-converters,那么它的转换器属性messageConverters也由这些子节点组成。

message-converters的子节点配置如下:

<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.example.MyHttpMessageConverter"/>
<bean class="org.example.MyOtherHttpMessageConverter"/>
mvc:message-converters>
mvc:annotation-driven>

2.message-converters子节点不存在或它的属性register-defaults为true的话,加入其他的转换器:ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter等。

我们看到这么一段:

这些boolean属性是哪里来的呢,它们是AnnotationDrivenBeanDefinitionParser的静态变量。

 其中ClassUtils中的isPresent方法如下:

看到这里,读者应该明白了为什么本文一开始在pom文件中需要加入对应的jackson依赖,为了让json转换器jackson成为默认转换器之一。

的作用读者也明白了。

下面我们看如何通过消息转换器将java对象进行转换的。

 

RequestMappingHandlerAdapter在进行handle的时候,会委托给HandlerMethod(具体由子类ServletInvocableHandlerMethod处理)的invokeAndHandle方法进行处理,这个方法又转接给HandlerMethodReturnValueHandlerComposite处理。

HandlerMethodReturnValueHandlerComposite维护了一个HandlerMethodReturnValueHandler列表。HandlerMethodReturnValueHandler是一个对返回值进行处理的策略接口,这个接口非常重要。关于这个接口的细节,请参考楼主的另外一篇博客:http://www.cnblogs.com/fangjian0423/p/springMVC-request-param-analysis.html。然后找到对应的HandlerMethodReturnValueHandler对结果值进行处理。

最终找到RequestResponseBodyMethodProcessor这个Handler(由于使用了@ResponseBody注解)。

RequestResponseBodyMethodProcessor的supportsReturnType方法:

然后使用handleReturnValue方法进行处理:

我们看到,这里使用了转换器。  

具体的转换方法:

至于为何是请求头部的Accept数据,读者可以进去debug这个getAcceptableMediaTypes方法看看。 我就不罗嗦了~~~

 ok。至此,我们走遍了所有的流程。

 

现在,回过头来看。为什么一开始的demo输出了json数据?

我们来分析吧。

 

由于我们只配置了,因此使用spring默认的那些转换器。

很明显,我们看到了2个xml和1个json转换器。 要看能不能转换,得看HttpMessageConverter接口的public boolean canWrite(Class clazz, MediaType mediaType)方法是否返回true来决定的。

我们先分析SourceHttpMessageConverter:

它的canWrite方法被父类AbstractHttpMessageConverter重写了。

发现SUPPORTED_CLASSES中没有Map类(本文demo返回的是Map类),因此不支持。

下面看Jaxb2RootElementHttpMessageConverter:

这个类直接重写了canWrite方法。

需要有XmlRootElement注解。 很明显,Map类当然没有。

最终MappingJackson2HttpMessageConverter匹配,进行json转换。(为何匹配,请读者自行查看源码)

实例讲解

 我们分析了转换器的转换过程之后,下面就通过实例来验证我们的结论吧。

首先,我们先把xml转换器实现。

之前已经分析,默认的转换器中是支持xml的。下面我们加上注解试试吧。

由于Map是jdk源码中的部分,因此我们用Employee来做demo。

因此,Controller加上一个方法:

@RequestMapping("/xmlOrJsonSimple")
@ResponseBody
public Employee xmlOrJsonSimple() {
return employeeService.getById(1);
}

实体中加上@XmlRootElement注解

结果如下:

我们发现,解析成了xml。

这里为什么解析成xml,而不解析成json呢?

 

之前分析过,消息转换器是根据class和mediaType决定的。

我们使用firebug看到:

我们发现Accept有xml,没有json。因此解析成xml了。

 

我们再来验证,同一地址,HTTP头部不同Accept。看是否正确。

$.ajax({
url: "${request.contextPath}/employee/xmlOrJsonSimple",
success: function(res) {
console.log(res);
},
headers: {
"Accept": "application/xml"
}
});

$.ajax({
url: "${request.contextPath}/employee/xmlOrJsonSimple",
success: function(res) {
console.log(res);
},
headers: {
"Accept": "application/json"
}
});

验证成功。

关于配置

如果不想使用中默认的RequestMappingHandlerAdapter的话,我们可以在重新定义这个bean,spring会覆盖掉默认的RequestMappingHandlerAdapter。

为何会覆盖,请参考楼主的另外一篇博客:http://www.cnblogs.com/fangjian0423/p/spring-Ordered-interface.html

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
list>
property>
bean>

或者如果只想换messageConverters的话。

<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.example.MyHttpMessageConverter"/>
<bean class="org.example.MyOtherHttpMessageConverter"/>
mvc:message-converters>
mvc:annotation-driven>

如果还想用其他converters的话。

以上是spring-mvc jar包中的converters。

这里我们使用转换xml的MarshallingHttpMessageConverter。

这个converter里面使用了marshaller进行转换

我们这里使用XStreamMarshaller。  

json没有转换器,返回406.

至于xml格式的问题,大家自行解决吧。 这里用的是XStream~。

使用这种方式,pom别忘记了加入xstream的依赖:

<dependency>
<groupId>com.thoughtworks.xstreamgroupId>
<artifactId>xstreamartifactId>
<version>1.4.7version>
dependency>

总结

 写了这么多,可能读者觉得有点罗嗦。 毕竟这也是自己的一些心得,希望都能说出来与读者共享。

刚接触SpringMVC的时候,发现这种自动转换机制很牛逼,但是一直没有研究它的原理,目前,算是了了一个小小心愿吧,SpringMVC还有很多内容,以后自己研究其他内容的时候还会与大家一起共享的。

文章难免会出现一些错误,希望读者们能指明出来。

参考资料

http://my.oschina.net/HeliosFly/blog/205343

http://my.oschina.net/lichhao/blog/172562

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html


推荐阅读
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 将Web服务部署到Tomcat
    本文介绍了如何在JDeveloper 12c中创建一个Java项目,并将其打包为Web服务,然后部署到Tomcat服务器。内容涵盖从项目创建、编写Web服务代码、配置相关XML文件到最终的本地部署和验证。 ... [详细]
  • ASP.NET MVC中Area机制的实现与优化
    本文探讨了在ASP.NET MVC框架中,如何通过Area机制有效地组织和管理大规模应用程序的不同功能模块。通过合理的文件夹结构和命名规则,开发人员可以更高效地管理和扩展项目。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • Struts与Spring框架的集成指南
    本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 尽管某些细分市场如WAN优化表现不佳,但全球运营商路由器和交换机市场持续增长。根据最新研究,该市场预计在2023年达到202亿美元的规模。 ... [详细]
  • ImmutableX Poised to Pioneer Web3 Gaming Revolution
    ImmutableX is set to spearhead the evolution of Web3 gaming, with its innovative technologies and strategic partnerships driving significant advancements in the industry. ... [详细]
  • 如何高效创建和使用字体图标
    在Web和移动开发中,为什么选择字体图标?主要原因是其卓越的性能,可以显著减少HTTP请求并优化页面加载速度。本文详细介绍了从设计到应用的字体图标制作流程,并提供了专业建议。 ... [详细]
  • 本文探讨了dbforms框架的核心设计理念及其背后的技术原理,详细分析了该框架如何通过其独特的设计模式来简化开发流程,并为开发者提供了优化使用方法的建议。 ... [详细]
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
author-avatar
君君-你好
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有