SpringMVC Study on the principle of automatic conversion of json and xml [with source code analysis]

Source: Internet
Author: User
Tags map class

Preface

SpringMVC is one of the mainstream Web MVC frameworks.

If you are not familiar with it, please refer to its entry blog: http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html

The article is a little long. Please read it with patience.

Symptom

The demo used in this article is based on maven and continues to be written based on the example of the Getting Started blog.

Let's take a look at the corresponding phenomenon. The key configuration in the configuration file *-dispatcher. xml is as follows (other common configuration files are not described. refer to the Getting Started blog mentioned at the beginning of this article ):

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

The following dependencies are required in pom (Spring dependencies and other dependencies are not displayed ):

        <dependency>            <groupId>org.codehaus.jackson</groupId>            <artifactId>jackson-core-asl</artifactId>            <version>1.9.13</version>        </dependency>        <dependency>            <groupId>org.codehaus.jackson</groupId>            <artifactId>jackson-mapper-asl</artifactId>            <version>1.9.13</version>        </dependency>

This dependency is serialized in json format.

OK. Add a method in the Controller:

@RequestMapping("/xmlOrJson")@ResponseBodypublic Map<String, Object> xmlOrJson() {    Map<String, Object> map = new HashMap<String, Object>();    map.put("list", employeeService.list());    return map;}

Direct access address:

We can see that there are just a few lines of configuration. After the @ ResponseBody annotation is used, the object returned by the Controller is automatically converted to the corresponding json data. Here, I have to lament the strength of SpringMVC.

We don't seem to see the specific configuration either. The only thing we see is the configuration in *-dispatcher. xml: <mvc: annotation-driven/>. This configuration causes the java object to be automatically converted to a json object.

So how does spring implement automatic conversion from a java object to a json object? Why is it converted to json data? What should I do if I want to convert to xml data?

Source code analysis

The spring version used in this article is 4.0.2.

Before explaining the <mvc: annotation-driven/> Configuration, we should first understand the message conversion mechanism of Spring. @ ResponseBody: the message conversion mechanism is used to convert the annotation to json data through the json converter.

The HttpMessageConverter interface is the http message conversion interface provided by Spring. For more information, see the second link in "references.

The following describes the configuration of <mvc: annotation-driven/>:

The parsing class of this Code in spring is:

In the 152-line parse method of the AnnotationDrivenBeanDefinitionParser source code:

The RequestMappingHandlerMapping, ableablewebbindinginitializer, and RequestMappingHandlerAdapter classes are instantiated respectively.

WhereRequestMappingHandlerMapping and RequestMappingHandlerAdapterThese two classes are important.

RequestMappingHandlerMapping processes request ing and processes the relationship between @ RequestMapping and request address.

RequestMappingHandlerAdapter is the request processing adapter, that is, the execution of the specific logic after the request is processed. It is related to the method of the class and the converter. This class is the focus of our discussion, the messageConverters attribute is the focus of this article.

Private method: getMessageConverters

From the code, we can set the logic of messageConverters in RequestMappingHandlerAdapter:

1. If the <mvc: annotation-driven> node has a subnode message-converters, its converter attribute messageConverters is also composed of these subnodes.

The subnode configuration of message-converters is as follows:

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

 

2. If the message-converters subnode does not exist or its attribute register-defaults is true, add other converters: ByteArrayHttpMessageConverter, StringHttpMessageConverter, ResourceHttpMessageConverter, and so on.

We can see this section:

Where are these boolean attributes? They are static variables of AnnotationDrivenBeanDefinitionParser.

The isPresent method in ClassUtils is as follows:

Here, the reader should understand why the corresponding jackson dependency needs to be added to the pom file at the beginning of this article, in order to make the json converter jackson one of the default converters.

<Mvc: annotation-driven>.

The following describes how to convert a java object through a message converter.

 

The RequestMappingHandlerAdapter delegates the handle method to the invokeAndHandle method of the HandlerMethod (which is specifically handled by the subclass ServletInvocableHandlerMethod). This method is then transferred to HandlerMethodReturnValueHandlerComposite for processing.

HandlerMethodReturnValueHandlerComposite maintains a list of HandlerMethodReturnValueHandler. HandlerMethodReturnValueHandler is a policy interface for processing returned values. Then find the correct HandlerMethodReturnValueHandler to process the result value.

Finally, find the Handler RequestResponseBodyMethodProcessor (because the @ ResponseBody annotation is used ).

The supportsReturnType method of RequestResponseBodyMethodProcessor is as follows:

Then the handleReturnValue method is used for processing:

We can see that the converter is used here.

Specific conversion method:

As to why is the Accept data in the request header, the reader can go to debug the getAcceptableMediaTypes method. I won't be jealous ~~~

OK. So far, we have gone through all the processes.

 

Now, let's look back. Why does the demo output json data at the beginning?

Let's analyze it.

 

Because only <mvc: annotation-driven> is configured, the default converters of spring are used.

Obviously, we see two xml and one json converter.It depends on whether it can be converted. It depends on the public boolean canWrite (Class <?> of the HttpMessageConverter interface. Clazz, MediaType mediaType) determines whether the method returns true.

First, analyze SourceHttpMessageConverter:

Its canWrite method is overwritten by the parent class AbstractHttpMessageConverter.

It is found that SUPPORTED_CLASSES does not contain the Map class (the Map class is returned in the demo in this article), so it is not supported.

The following describes Jaxb2RootElementHttpMessageConverter:

This class directly overrides the canWrite method.

XmlRootElement annotation is required. Obviously, the Map class does not.

Finally, MappingJackson2HttpMessageConverter matches and performs json conversion. (For the matching reason, please check the source code yourself)

Instance description

After analyzing the conversion process of the converter, let's use an example to verify our conclusion.

First, implement the xml converter.

Previously analyzed, the default converter supports xml. Next let's add an annotation.

Since Map is part of the jdk source code, we use Employee for demo.

Therefore, add a method to the Controller:

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

Add @ XmlRootElement annotation to the object

The result is as follows:

We found that it was parsed into xml.

Why is it parsed into xml instead of json?

 

Previously analyzed, the message converter is determined by class and mediaType.

We use firebug to see:

We found that the Accept has xml and no json. Therefore, it is parsed into xml.

 

Let's verify that the same address and HTTP header have different Accept. Check whether it is correct.

$.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"    }});

Verification Successful.

Configuration

If you do not want to use the default RequestMappingHandlerAdapter in <mvc: annotation-driven/>, You can redefine this bean and spring will overwrite the default RequestMappingHandlerAdapter.

    <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>

Or if you only want to change messageConverters.

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

If you want to use other converters.

The above is converters in the spring-mvc jar package.

Here we use MarshallingHttpMessageConverter to convert xml.

This converter uses marshaller for conversion.

Here we use xstreampolicaller.

NO converter in json format. 406 is returned.

As for the xml format, you can solve it on your own. XStream ~ is used here ~.

In this way, pom should not forget to add the xstream dependency:

<dependency>    <groupId>com.thoughtworks.xstream</groupId>    <artifactId>xstream</artifactId>    <version>1.4.7</version></dependency>
Summary

After writing so much, readers may feel a bit cool. After all, this is some of your own experiences, and I hope you can share it with readers.

When I first came into contact with SpringMVC, I found this automatic conversion mechanism awesome, but I have never studied its principles. At present, it's a little wish. SpringMVC has a lot to do with it, I will share it with you when I study other content myself in the future.

Some errors may inevitably occur in the article. I hope readers can make it clear.

References

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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.