Spring MVC HTTP Message Conversionhtml
在spring mvc中,一个http请求和响应流程以下图所示:java
其中 HttpMessageConvert 扮演了 http请求消息和响应消息进行转换和角色。ios
这里先说一下对于 HTTP 请求和响应的通常抽象。web
咱们知道,在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请求和响应流程中所起到的做用。
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===================