Spring MVC HTTP Message Conversion

Spring MVC HTTP Message Conversionhtml

在spring mvc中,一个http请求和响应流程以下图所示:java

其中 HttpMessageConvert 扮演了 http请求消息和响应消息进行转换和角色。ios

这里先说一下对于 HTTP 请求和响应的通常抽象。web

 

HTTP 请求和响应的封装抽象

咱们知道,在servlet标准中,能够用javax.servlet.ServletRequest 接口中的如下方法:spring

public ServletInputStream getInputStream() throws IOException;

来获得一个ServletInputStream。这个ServletInputStream中,能够读取到一个原始请求报文的全部内容。api

一样的,在javax.servlet.ServletResponse 接口中,能够用如下方法:mvc

public ServletOutputStream getOutputStream() throws IOException;

来获得一个ServletOutputStream,这个ServletOutputSteam,继承自java中的OutputStream,可让你输出Http的响应报文内容。app

让咱们尝试着像SpringMVC的设计者同样来思考一下。咱们知道,Http请求和响应报文本质上都是一串字符串,当请求报文来到java世界,它会被封装成为一个ServletInputStream的输入流,供咱们读取报文。响应报文则是经过一个ServletOutputStream的输出流,来输出响应报文。this

 

而在 SpringMVC 中则提供了如下两个类来进行更高层的对 http 请求和响应的封装抽象。url

HttpInputMessage

这个类是SpringMVC内部对一次Http请求报文的抽象 ,在HttpMessageConverter的read()方法中,有一个HttpInputMessage的形参,它正是SpringMVC的消息转换器所做用的受体“请求消息”的内部抽象,消息转换器从“请求消息”中按照规则提取消息,转换为方法形参中声明的对象。

package org.springframework.http;
import java.io.IOException;
import java.io.InputStream;
public interface HttpInputMessage extends HttpMessage {
    InputStream getBody() throws IOException;
}

HttpOutputMessage

这个类是SpringMVC内部对一次Http响应报文的抽象  ,在HttpMessageConverter的write()方法中,有一个HttpOutputMessage的形参,它正是SpringMVC的消息转换器所做用的受体“响应消息”的内部抽象,消息转换器将“响应消息”按照必定的规则写到响应报文中。

package org.springframework.http;
import java.io.IOException;
import java.io.OutputStream;
public interface HttpOutputMessage extends HttpMessage {
    OutputStream getBody() throws IOException;
}

如今再回来讲一下 HttpMessageConvert 在SpringMVC的http请求和响应流程中所起到的做用。

 

HttpMessageConvert-HTTP消息转换器

HttpMessageConverter接口描述:

public interface HttpMessageConverter<T> {

    // Indicate whether the given class and media type can be read by this converter.
    boolean canRead(Class<?> clazz, MediaType mediaType);

    // Indicate whether the given class and media type can be written by this converter.
    boolean canWrite(Class<?> clazz, MediaType mediaType);

    // Return the list of MediaType objects supported by this converter.
    List<MediaType> getSupportedMediaTypes();

    // Read an object of the given type from the given input message, and returns it.
    T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;

    // Write an given object to the given output message.
    void write(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;

}

那么springmvc是如何实例化HttpMessageConvert的呢?这就是<mvc:annotation-driven/>标签的做用了。

咱们通常要在 spirngmvc的配置文件中加这个标签,这个标签的实现类就是

org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser

经过注释文档你能够看到它的主要做用是:

1.注入HandlerMapping(用来处理请求映射的。其中第一个是处理@RequestMapping注解的。第二个会将controller类的名字映射为请求url。)

* <p>This class registers the following {@link HandlerMapping}s:</p>
* <ul>
* <li>{@link RequestMappingHandlerMapping}
* ordered at 0 for mapping requests to annotated controller methods.
* <li>{@link BeanNameUrlHandlerMapping}
* ordered at 2 to map URL paths to controller bean names.
* </ul>

2.注入HandlerAdapter(RequestMappingHandlerAdapter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter三个是用来处理请求的。具体点说就是肯定调用哪一个controller的哪一个方法来处理当前请求。第一个处理@Controller注解的处理器,支持自定义方法参数和返回值(很酷)。第二个是处理继承HttpRequestHandler的处理器。第三个处理继承自Controller接口的处理器。)

* <p>This class registers the following {@link HandlerAdapter}s:
* <ul>
* <li>{@link RequestMappingHandlerAdapter}
* for processing requests with annotated controller methods.
* <li>{@link HttpRequestHandlerAdapter}
* for processing requests with {@link HttpRequestHandler}s.
* <li>{@link SimpleControllerHandlerAdapter}
* for processing requests with interface-based {@link Controller}s.
* </ul>

3.注入HandlerExceptionResolver(处理异常)

* <p>This class registers the following {@link HandlerExceptionResolver}s:
* <ul>
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
* through @{@link ExceptionHandler} methods.
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
* with @{@link ResponseStatus}.
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
* exception types
* </ul>

4.注入AntPathMatcher和UrlPathHelper

* <p>This class registers an {@link org.springframework.util.AntPathMatcher}
* and a {@link org.springframework.web.util.UrlPathHelper} to be used by:
* <ul>
* <li>the {@link RequestMappingHandlerMapping},
* <li>the {@link HandlerMapping} for ViewControllers
* <li>and the {@link HandlerMapping} for serving resources
* </ul>
* Note that those beans can be configured by using the {@code path-matching} MVC namespace element.

更详细的能够读api doc。

好了,那么HttpMessageConvert又是从哪里注入的呢?别急,经过读AnnotationDrivenBeanDefinitionParser类的代码,发现messageConverters是在注入RequestMappingHandlerAdapter时实例化的,具体的代码以下,

RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
addResponseBodyAdvice(handlerAdapterDef);

handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);

咱们就来看看这个标签默认为咱们实例化了哪些 HttpMessageConvert。代码以下,

private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {
    Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
    ManagedList<? super Object> messageConverters = new ManagedList<Object>();
    if (convertersElement != null) {
        messageConverters.setSource(source);
        for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
            Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
            messageConverters.add(object);
        }
    }

    if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
        messageConverters.setSource(source);
        messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));

        RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);
        stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
        messageConverters.add(stringConverterDef);

        messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));
        messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));
        messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));

        if (romePresent) {
            messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));
            messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));
        }

        if (jackson2XmlPresent) {
            RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source);
            GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
            jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
            jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
            messageConverters.add(jacksonConverterDef);
        }
        else if (jaxb2Present) {
            messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
        }

        if (jackson2Present) {
            RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2HttpMessageConverter.class, source);
            GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
            jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
            messageConverters.add(jacksonConverterDef);
        }
        else if (gsonPresent) {
            messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));
        }
    }
    return messageConverters;
}

经过上面的代码,能够很清楚的看到默认实例化的HttpMessageConvert以下:

  • ByteArrayHttpMessageConverter

  • StringHttpMessageConverter

  • ResourceHttpMessageConverter

  • SourceHttpMessageConverter

  • AllEncompassingFormHttpMessageConverter

若是相应的消息转换类库在classpath下而且可以被加载(classload),那么如下convert也会默认实例化,

  • AtomFeedHttpMessageConverter

  • RssChannelHttpMessageConverter

  • MappingJackson2XmlHttpMessageConverter

  • Jaxb2RootElementHttpMessageConverter

  • MappingJackson2HttpMessageConverter

  • GsonHttpMessageConverter

哈哈,齐全了!有这些convert转换消息仍是不够的。它是如何选择一个具体的convert来转换消息的呢?

HttpMessageConverter接口的定义出现了成对的canRead(),read()和canWrite(),write()方法,MediaType是对请求的MediaType属性的封装。

举个例子,当咱们声明了下面这个处理方法。

@RequestMapping(value="/string", method=RequestMethod.POST)
public @ResponseBody String readString(@RequestBody String string) {
    return "Read string '" + string + "'";
}

在SpringMVC进入readString方法前,会根据@RequestBody注解选择适当的HttpMessageConverter实现类来将请求参数解析到string变量中,具体来讲是使用了StringHttpMessageConverter类,它的canRead()方法返回true,而后它的read()方法会从请求中读出请求参数,绑定到readString()方法的string变量中。

当SpringMVC执行readString方法后,因为返回值标识了@ResponseBody,SpringMVC将使用StringHttpMessageConverter的write()方法,将结果做为String值写入响应报文,固然,此时canWrite()方法返回true。

这个转换过程就是本文一开始贴的图片。将上述过程集中描述的一个类是org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor,这个类同时实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler两个接口。前者是将请求报文绑定处处理方法形参的策略接口,后者则是对处理方法返回值进行处理的策略接口。两个接口的源码以下:

package org.springframework.web.method.support;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter parameter);
    Object resolveArgument(MethodParameter parameter,
                           ModelAndViewContainer mavContainer,
                           NativeWebRequest webRequest,
                           WebDataBinderFactory binderFactory) throws Exception;
}
package org.springframework.web.method.support;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
public interface HandlerMethodReturnValueHandler {
    boolean supportsReturnType(MethodParameter returnType);
    void handleReturnValue(Object returnValue,
                           MethodParameter returnType,
                           ModelAndViewContainer mavContainer,
                           NativeWebRequest webRequest) throws Exception;
}

RequestResponseBodyMethodProcessor这个类,同时充当了方法参数解析和返回值处理两种角色。

参考引用:http://my.oschina.net/lichhao/blog/172562

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

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

===================END===================

相关文章
相关标签/搜索