abstract
summary
In spring MVC, the @ RequestBody and @ ResponseBody annotations can be used to complete the conversion from request message to object and object to response message respectively. The underlying flexible message conversion mechanism is spring 3 The newly introduced HttpMessageConverter in X is the message converter mechanism.
Abstraction of Http requests
Or go back to the request response, that is, parse the request body and then return the response message, which is the most basic Http request process. As we know, in the servlet standard, you can use javax servlet. The following methods in the ServletRequest interface:
public ServletInputStream getInputStream() throws IOException;
Get a ServletInputStream. In this ServletInputStream, all contents of an original request message can be read. Similarly, in javax servlet. In the servletresponse interface, you can use the following methods:
public ServletOutputStream getOutputStream() throws IOException;
Get a ServletOutputStream. This ServletOutputStream inherits from the OutputStream in java and allows you to output the response message content of Http.
Let's try to think like the designers of spring MVC. We know that Http request and response messages are essentially a string. When the request message comes to the java world, it will be encapsulated into an input stream of ServletInputStream for us to read. The response message outputs the response message through the output stream of a ServletOutputStream.
We can only read the original string message from the stream. Similarly, we can only write the original characters to the output stream. In the java world, the processing of business logic takes each object with business significance as the processing dimension, so there is an impedance problem from string to java object when the message arrives at and leaves spring MVC. This process cannot be converted manually by developers. We know that in struts 2, OGNL is used to deal with this problem, while in spring MVC, it is the HttpMessageConverter mechanism. Let's look at two interfaces first.
HttpInputMessage
This class is the internal abstraction of an Http request message in spring MVC. In the read() method of HttpMessageConverter, there is a formal parameter of HttpInputMessage, which is the internal abstraction of the receptor "request message" acted by the message converter of spring MVC. The message converter extracts the message from the "request message" according to the rules and converts it into the object declared in the method formal parameter.
package org.springframework.http; import java.io.IOException; import java.io.InputStream; public interface HttpInputMessage extends HttpMessage { InputStream getBody() throws IOException; }
HttpOutputMessage
This class is the internal abstraction of spring MVC for an Http response message. In the write() method of HttpMessageConverter, there is a formal parameter of HttpOutputMessage, which is the internal abstraction of the receptor "response message" acted by the message converter of spring MVC. The message converter writes the "response message" to the response message according to certain rules.
package org.springframework.http; import java.io.IOException; import java.io.InputStream; public interface HttpInputMessage extends HttpMessage { InputStream getBody() throws IOException; }
HttpMessageConverter
The highest-level interface abstraction of message converter describes the general characteristics of a message converter. We can understand spring 3.0 from the methods defined in this interface The thinking process of the designer of X on this mechanism.
package org.springframework.http.converter; import java.io.IOException; import java.util.List; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; public interface HttpMessageConverter<T> { boolean canRead(Class<?> clazz, MediaType mediaType); boolean canWrite(Class<?> clazz, MediaType mediaType); List<MediaType> getSupportedMediaTypes(); T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; }
Before spring MVC enters the readString method, it will select the appropriate HttpMessageConverter implementation class according to the @ RequestBody annotation to parse the request parameters into the string variable. Specifically, the StringHttpMessageConverter class is used. Its canRead() method returns true, and then its read() method will read the request parameters from the request, Bind to the string variable of the readString() method.
After spring MVC executes the readString method, because the return value identifies @ ResponseBody, spring MVC will use the write() method of StringHttpMessageConverter to write the result into the response message as a String value. Of course, the canWrite() method returns true at this time.
We can use the following figure to briefly describe this process.
RequestResponseBodyMethodProcessor
One of the classes described in the above process set is org springframework. web. servlet. mvc. method. annotation. Requestresponsebodymethodprocessor. This class implements two interfaces: HandlerMethodArgumentResolver and HandlerMethodReturnValueHandler. The former is the policy interface that binds the request message to the formal parameters of the processing method, and the latter is the policy interface that processes the return value of the processing method. The source code of the two interfaces is as follows:
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; }
The RequestResponseBodyMethodProcessor class plays two roles: method parameter parsing and return value processing. From its source code, we can find the method implementation of the above two interfaces.
Implementation of HandlerMethodArgumentResolver interface:
public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType()); String name = Conventions.getVariableNameForParameter(parameter); WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name); if (argument != null) { validate(binder, parameter); } mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); return argument; }
Implementation of HandlerMethodReturnValueHandler interface
public boolean supportsReturnType(MethodParameter returnType) { return returnType.getMethodAnnotation(ResponseBody.class) != null; } public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException { mavContainer.setRequestHandled(true); if (returnValue != null) { writeWithMessageConverters(returnValue, returnType, webRequest); } }
After reading the above code, the context of the whole HttpMessageConverter message conversion has been very clear. Because the implementation of the two interfaces depends on whether there is @ RequestBody and @ ResponseBody, and then call HttpMessageConverter to read and write messages.
If you want to ask how to trace to the RequestResponseBodyMethodProcessor, please follow the ideas of the previous blog posts and come here spring-mvc-showcase Download the source code and debug the examples related to HttpMessageConverter. As long as you work hard, I believe you will have your own harvest.
reflection
When talking about the essence of wechat, Zhang Xiaolong said: "wechat is just a platform in which messages flow". During our analysis of spring MVC source code, we can understand a similar truth from the HttpMessageConverter mechanism. In the eyes of spring MVC designers, a request message and a response message are abstracted as a request message HttpInputMessage and a response message HttpOutputMessage respectively.
When processing the request, the appropriate message converter binds the request message to the formal parameter object in the method. Here, there may be many different message forms for the same object, such as json and xml. Similarly, when responding to a request, the return value of the method may also be returned in different message forms, such as json and xml.
In spring MVC, for different message forms, we have different HttpMessageConverter implementation classes to handle various message forms. However, as long as the "effective information" contained in these messages is consistent, different message converters will generate the same conversion results. As for the different parsing details among various messages, they are masked in different HttpMessageConverter implementation classes.