东莞专业网站设计,如何制作手机版网页,企业网站建设报价明细表,网站建设第三方平台目录
前言现象源码分析实例讲解关于配置总结参考资料 前言 SpringMVC是目前主流的Web MVC框架之一。 如果有同学对它不熟悉#xff0c;那么请参考它的入门blog#xff1a;http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 现象 本文使用的demo基于maven… 目录
前言现象源码分析实例讲解关于配置总结参考资料 前言 SpringMVC是目前主流的Web MVC框架之一。 如果有同学对它不熟悉那么请参考它的入门bloghttp://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-packageorg.format.demo.controller/pom中需要有以下依赖(Spring依赖及其他依赖不显示)
dependencygroupIdorg.codehaus.jackson/groupId artifactIdjackson-core-asl/artifactId version1.9.13/version /dependency dependency groupIdorg.codehaus.jackson/groupId artifactIdjackson-mapper-asl/artifactId version1.9.13/version /dependency 这个依赖是json序列化的依赖。 ok。我们在Controller中添加一个method RequestMapping(/xmlOrJson)
ResponseBody
public MapString, Object xmlOrJson() {MapString, Object map new HashMapString, Object();map.put(list, employeeService.list());return map;
} 直接访问地址 我们看到短短几行配置。使用ResponseBody注解之后Controller返回的对象 自动被转换成对应的json数据在这里不得不感叹SpringMVC的强大。 我们好像也没看到具体的配置唯一看到的就是*-dispatcher.xml中的一句配置mvc:annotation-driven/。其实就是这个配置导致了java对象自动转换成json对象的现象。 那么spring到底是如何实现java对象到json对象的自动转换的呢 为什么转换成了json数据如果想转换成xml数据那该怎么办 源码分析 本文使用的spring版本是4.0.2。 在讲解mvc:annotation-driven/这个配置之前我们先了解下Spring的消息转换机制。ResponseBody这个注解就是使用消息转换机制最终通过json的转换器转换成json数据的。 HttpMessageConverter接口就是Spring提供的http消息转换接口。有关这方面的知识大家可以参考参考资料中的第二条链接里面讲的很清楚。 下面开始分析mvc:annotation-driven/这句配置: 这句代码在spring中的解析类是 在AnnotationDrivenBeanDefinitionParser源码的152行parse方法中 分别实例化了RequestMappingHandlerMappingConfigurableWebBindingInitializerRequestMappingHandlerAdapter等诸多类。 其中RequestMappingHandlerMapping和RequestMappingHandlerAdapter这两个类比较重要。 RequestMappingHandlerMapping处理请求映射的处理RequestMapping跟请求地址之间的关系。 RequestMappingHandlerAdapter是请求处理的适配器也就是请求之后处理具体逻辑的执行关系到哪个类的哪个方法以及转换器等工作这个类是我们讲的重点其中它的属性messageConverters是本文要讲的重点。 私有方法:getMessageConverters 从代码中我们可以RequestMappingHandlerAdapter设置messageConverters的逻辑 1.如果mvc:annotation-driven节点有子节点message-converters那么它的转换器属性messageConverters也由这些子节点组成。 message-converters的子节点配置如下
mvc:annotation-drivenmvc:message-convertersbean classorg.example.MyHttpMessageConverter/ bean classorg.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成为默认转换器之一。 mvc:annotation-driven的作用读者也明白了。 下面我们看如何通过消息转换器将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数据 我们来分析吧。 由于我们只配置了mvc:annotation-driven因此使用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}
}); 验证成功。 关于配置 如果不想使用mvc:annotation-driven/中默认的RequestMappingHandlerAdapter的话我们可以在重新定义这个beanspring会覆盖掉默认的RequestMappingHandlerAdapter。 为何会覆盖请参考楼主的另外一篇博客http://www.cnblogs.com/fangjian0423/p/spring-Ordered-interface.html bean classorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapterproperty namemessageConverters list bean classorg.springframework.http.converter.ByteArrayHttpMessageConverter/ bean classorg.springframework.http.converter.StringHttpMessageConverter/ bean classorg.springframework.http.converter.ResourceHttpMessageConverter/ /list /property /bean 或者如果只想换messageConverters的话。
mvc:annotation-drivenmvc:message-convertersbean classorg.example.MyHttpMessageConverter/ bean classorg.example.MyOtherHttpMessageConverter/ /mvc:message-converters /mvc:annotation-driven 如果还想用其他converters的话。 以上是spring-mvc jar包中的converters。 这里我们使用转换xml的MarshallingHttpMessageConverter。 这个converter里面使用了marshaller进行转换 我们这里使用XStreamMarshaller。 json没有转换器返回406. 至于xml格式的问题大家自行解决吧。 这里用的是XStream。 使用这种方式pom别忘记了加入xstream的依赖
dependencygroupIdcom.thoughtworks.xstream/groupId artifactIdxstream/artifactId version1.4.7/version /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