該系列文檔是本人在學習 Spring MVC 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀html
Spring 版本:5.2.4.RELEASEjava
該系列其餘文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》git
HandlerAdapter 組件,處理器的適配器。由於處理器 handler
的類型是 Object 類型,須要有一個調用者來實現 handler
是怎麼被執行。Spring 中的處理器的實現多變,好比用戶的處理器能夠實現 Controller 接口或者 HttpRequestHandler 接口,也能夠用 @RequestMapping
註解將方法做爲一個處理器等,這就致使 Spring MVC 沒法直接執行這個處理器。因此這裏須要一個處理器適配器,由它去執行處理器github
因爲 HandlerMapping 組件涉及到的內容較多,考慮到內容的排版,因此將這部份內容拆分紅了五個模塊,依次進行分析:web
本文是接着《HandlerAdapter 組件(四)之 HandlerMethodReturnValueHandler》一文來分享 HttpMessageConverter 組件。在 HandlerAdapter
執行處理器的過程當中,具體的執行過程交由 ServletInvocableHandlerMethod
對象來完成,其中須要先經過 HandlerMethodArgumentResolver
參數解析器從請求中解析出方法的入參,而後再經過反射機制調用對應的方法,獲取到執行結果後須要經過 HandlerMethodReturnValueHandler
結果處理器來進行處理。在處理返回結果的過程當中,可能須要經過 HttpMessageConverter 消息轉換器將返回結果設置到響應體中,固然也可能須要經過它從請求體獲取入參。spring
在使用 Spring MVC 時,@RequestBody
和 @ResponseBody
兩個註解,分別完成請求報文到 Java 對象、Java 對象到響應報文的轉換,底層的實現就是經過 Spring 3.x 中引入的 HttpMessageConverter 消息轉換機制來實現的。編程
再開始閱讀本文以前,先來理解一些概念。在處理 HTTP 請求的過程當中,須要解析請求體,返回結果設置到響應體。在 Servlet 標準中,javax.servlet.ServletRequest
和 javax.servlet.ServletResponse
分別有有如下方法:json
// javax.servlet.ServletRequest public ServletInputStream getInputStream() throws IOException; // javax.servlet.ServletResponse public ServletOutputStream getOutputStream() throws IOException;
經過上面兩個方法能夠獲取到請求體和響應體,ServletInputStream 和 ServletOutputStream 分別繼承 java 中的 InputStream 和 OutputStream 流對象,能夠經過它們獲取請求報文和設置響應報文。咱們只能從流中讀取原始的字符串報文,或者往流中寫入原始的字符串,而 Java 是面向對象編程的,字符串與 Java 對象之間的轉換不可能交由開發者去實現。在 Sping MVC 中,會將 Servlet 提供的請求和響應進行一層抽象封裝,便於操做讀取和寫入,再經過 HttpMessageConverter 消息轉換機制來解析請求報文或者設置響應報文。數組
先來回顧一下 HandlerMethodReturnValueHandler
如何處理放回結果的,能夠回到 《HandlerAdapter 組件(四)之 HandlerMethodReturnValueHandler》 中 RequestResponseBodyMethodProcessor 小節下面的 handleReturnValue
方法和 writeWithMessageConverters
方法微信
// RequestResponseBodyMethodProcessor.java @Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { // <1> 設置已處理 mavContainer.setRequestHandled(true); // <2> 建立請求和響應 ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // Try even with null return value. ResponseBodyAdvice could get involved. // <3> 使用 HttpMessageConverter 對對象進行轉換,並寫入到響應 writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); } // AbstractMessageConverterMethodProcessor.java protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Assert.state(servletRequest != null, "No HttpServletRequest"); return new ServletServerHttpRequest(servletRequest); } // AbstractMessageConverterMethodProcessor.java protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) { HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); Assert.state(response != null, "No HttpServletResponse"); return new ServletServerHttpResponse(response); }
上面會將請求封裝成 ServletServerHttpRequest 和 ServletServerHttpResponse 對象
上面這些接口定義了一些獲取請求和設置響應相關信息的方法,便於獲取請求和設置響應
// AbstractMessageConverterMethodProcessor.java protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { // <1> 得到 body、valueType、targetType Object body; Class<?> valueType; Type targetType; // <3> 選擇使用的 MediaType MediaType selectedMediaType = null; // <4> 若是匹配到,則進行寫入邏輯 if (selectedMediaType != null) { // <4.1> 移除 quality 。例如,application/json;q=0.8 移除後爲 application/json selectedMediaType = selectedMediaType.removeQualityValue(); // <4.2> 遍歷 messageConverters 數組 for (HttpMessageConverter<?> converter : this.messageConverters) { // <4.3> 判斷 HttpMessageConverter 是否支持轉換目標類型 GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { // <5.2> body 非空,則進行寫入 if (body != null) { if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } // <5.4> return 返回,結束整個邏輯 return; } } } // ... 上面省略了大量代碼 }
<4.2>
處,遍歷全部的 HttpMessageConverter 實現類
<4.3>
處,調用當前 HttpMessageConverter 實現類的 canWrite
方法,判斷是否支持寫入
<5.2>
處,調用該 HttpMessageConverter 實現類的 write
方法,進行寫入
org.springframework.http.HttpInputMessage
:對一次 Http 請求報文的抽象
public interface HttpInputMessage extends HttpMessage { /** * Return the body of the message as an input stream. * @return the input stream body (never {@code null}) * @throws IOException in case of I/O errors */ InputStream getBody() throws IOException; }
在 HttpMessageConverter 的 read
方法中,有一個 HttpInputMessage 的形參,它正是 Spring MVC 的消息轉換器所做用的受體請求消息的內部抽象,消息轉換器從請求消息中按照規則提取消息,轉換爲方法形參中聲明的對象。
org.springframework.http.HttpOutputMessage
:對一次 Http 響應報文的抽象
public interface HttpOutputMessage extends HttpMessage { /** * Return the body of the message as an output stream. * @return the output stream body (never {@code null}) * @throws IOException in case of I/O errors */ OutputStream getBody() throws IOException; }
在 HttpMessageConverter 的 write
方法中,有一個 HttpOutputMessage 的形參,它正是 Spring MVC 的消息轉換器所做用的受體響應消息的內部抽象,消息轉換器將響應消息按照必定的規則寫到響應報文中
org.springframework.http.converter.HttpMessageConverter
:對消息轉換器最高層次的接口抽象,描述了一個消息轉換器的通常特徵
public interface HttpMessageConverter<T> { /** 可否讀取 */ boolean canRead(Class<?> clazz, @Nullable MediaType mediaType); /** 可以寫入 */ boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); /** 獲取支持的 MediaType */ List<MediaType> getSupportedMediaTypes(); /** 讀取請求體 */ T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; /** 設置響應體 */ void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; }
HttpMessageConverter 接口體系的結構以下:
上圖只列出了部分實現類,由於在 Spring MVC 和 Sping Boot 中默認的 HttpMessageConverter 實現類差很少就上面幾個,咱們來看看有哪些實現類:
Spring MVC
Spring Boot
@RestController public class UserController { @Autowired UserService userService; /** 這裏的 @RequestBody 標註入參僅示例,是爲了後續的分析 */ @GetMapping(value = "/query") public Result<List<User>> queryUser(@RequestBody String name) { try { return Result.success().data(userService.queryUserByName(name)); } catch (Exception e) { return Result.fail(e); } } }
當你發起一個 HTTP 請求 GET /query
,由於你添加了@RequestBody 註解,因此是從請求體讀請求報文的,能夠設置請求體中的數據爲 ming
,也就是我想要拿到名字爲 ming
的全部用戶的信息
Spring MVC 處理該請求時,會先進入到 RequestResponseBodyMethodProcessor 這個類,獲取方法入參,其中會經過 StringHttpMessageConverter
從請求體中讀取 ming
數據,做爲調用方法的入參。
在 Spring MVC 獲取到方法的返回結果後,又會進入到 RequestResponseBodyMethodProcessor 這個類,往響應體中寫數據,其中會經過 MappingJackson2HttpMessageConverter
將 List<User>
返回結果寫入響應。
提示:RequestResponseBodyMethodProcessor 既是參數解析器,也是返回結果處理器
總結下來,整個過程以下所示:
org.springframework.http.converter.AbstractHttpMessageConverter
,實現 HttpMessageConverter 接口,提供通用的骨架方法
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> { /** * 支持的 MediaType */ private List<MediaType> supportedMediaTypes = Collections.emptyList(); /** * 默認的字符集 */ @Nullable private Charset defaultCharset; protected AbstractHttpMessageConverter() { } protected AbstractHttpMessageConverter(MediaType supportedMediaType) { setSupportedMediaTypes(Collections.singletonList(supportedMediaType)); } protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) { setSupportedMediaTypes(Arrays.asList(supportedMediaTypes)); } protected AbstractHttpMessageConverter(Charset defaultCharset, MediaType... supportedMediaTypes) { this.defaultCharset = defaultCharset; setSupportedMediaTypes(Arrays.asList(supportedMediaTypes)); } }
supportedMediaTypes
:支持的 MediaType
defaultCharset
:默認的字符集
上面兩個屬性能夠由子類去設置
實現 getSupportedMediaTypes()
方法,得到支持的 MediaType,以下:
@Override public List<MediaType> getSupportedMediaTypes() { return Collections.unmodifiableList(this.supportedMediaTypes); }
實現 canRead(Class<?> clazz, @Nullable MediaType mediaType)
方法,是否支持從請求中讀取該類型的方法參數,以下:
@Override public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canRead(mediaType); } protected abstract boolean supports(Class<?> clazz); protected boolean canRead(@Nullable MediaType mediaType) { if (mediaType == null) { return true; } for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.includes(mediaType)) { return true; } } return false; }
其中 supports(Class<?> clazz)
抽象方法,交由子類去實現
實現 canWrite(Class<?> clazz, @Nullable MediaType mediaType)
方法,是否支持往響應中寫入該類型的返回結果,以下:
@Override public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canWrite(mediaType); } protected abstract boolean supports(Class<?> clazz); protected boolean canWrite(@Nullable MediaType mediaType) { if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) { return true; } for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.isCompatibleWith(mediaType)) { return true; } } return false; }
其中 supports(Class<?> clazz)
抽象方法,交由子類去實現
實現 read(Class<? extends T> clazz, HttpInputMessage inputMessage)
方法,從請求中讀取該類型的方法參數,以下:
@Override public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return readInternal(clazz, inputMessage); } protected abstract T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
其中 readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)
抽象方法,交由子類去實現
實現 write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
方法,往響應中寫入該類型的返回結果,以下:
@Override public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { // <1> 獲取響應頭 final HttpHeaders headers = outputMessage.getHeaders(); // <2> 若是 Content-Type 爲空則設置默認的 addDefaultHeaders(headers, t, contentType); // <3> 往響應中寫入數據 if (outputMessage instanceof StreamingHttpOutputMessage) { // <3.1> 若是是流,則再封裝一層 StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() { @Override public OutputStream getBody() { return outputStream; } @Override public HttpHeaders getHeaders() { return headers; } })); } else { // <3.2> 普通對象 writeInternal(t, outputMessage); outputMessage.getBody().flush(); } } protected abstract void writeInternal(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
獲取響應頭
若是 Content-Type
爲空則設置默認的
往響應中寫入數據
writeInternal(T t, HttpOutputMessage outputMessage)
抽象方法刷出流
org.springframework.http.converter.StringHttpMessageConverter
,繼承 AbstractHttpMessageConverter 抽象類,String 類型的消息轉換器
實現 supports(Class<?> clazz)
方法,是否支持從請求中讀取該類型的方法參數,或者是否支持往響應中寫入該類型的返回結果,以下:
@Override public boolean supports(Class<?> clazz) { return String.class == clazz; }
是 String
類就能夠,因此在示例中,會使用 StringHttpMessageConverter
消息轉換器
實現 readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage)
方法,從請求中讀取該類型的方法參數,以下:
@Override protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException { Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType()); return StreamUtils.copyToString(inputMessage.getBody(), charset); } // org.springframework.util.StreamUtils.java public static String copyToString(@Nullable InputStream in, Charset charset) throws IOException { if (in == null) { return ""; } StringBuilder out = new StringBuilder(); InputStreamReader reader = new InputStreamReader(in, charset); char[] buffer = new char[BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = reader.read(buffer)) != -1) { out.append(buffer, 0, bytesRead); } return out.toString(); }
邏輯不復雜,直接從請求的 ServletInputStream 流中讀取出來,轉換成字符串
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter
:繼承 AbstractGenericHttpMessageConverter 抽象類,JSON 格式的消息讀取或者寫入,也就是咱們熟悉的 @RequestBody
和 @ResponseBody
註解對應的 HttpMessageConverter 消息轉換器
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> { /** * The default charset used by the converter. */ public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; protected ObjectMapper objectMapper; @Nullable private Boolean prettyPrint; @Nullable private PrettyPrinter ssePrettyPrinter; protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) { this.objectMapper = objectMapper; setDefaultCharset(DEFAULT_CHARSET); DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:")); this.ssePrettyPrinter = prettyPrinter; } protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType supportedMediaType) { this(objectMapper); setSupportedMediaTypes(Collections.singletonList(supportedMediaType)); } protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes) { this(objectMapper); setSupportedMediaTypes(Arrays.asList(supportedMediaTypes)); } }
實現 canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType)
方法,是否支持從請求中讀取該類型的方法參數,以下:
@Override public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) { if (!canRead(mediaType)) { return false; } // 得到方法入參的類型 JavaType javaType = getJavaType(type, contextClass); AtomicReference<Throwable> causeRef = new AtomicReference<>(); // 經過 ObjectMapper 判斷是否可以反序列化 if (this.objectMapper.canDeserialize(javaType, causeRef)) { return true; } logWarningIfNecessary(javaType, causeRef.get()); return false; }
實現 read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
方法,從請求中讀取該類型的方法參數,以下:
@Override public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { // 得到方法入參的類型 JavaType javaType = getJavaType(type, contextClass); // 從請求中讀取該類型的方法入參 return readJavaType(javaType, inputMessage); } private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException { try { // 若是請求是 MappingJacksonInputMessage 類型,默認不是 if (inputMessage instanceof MappingJacksonInputMessage) { Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView(); if (deserializationView != null) { return this.objectMapper.readerWithView(deserializationView).forType(javaType). readValue(inputMessage.getBody()); } } // 經過 ObjectMapper 從請求中讀取該類型的方法入參 return this.objectMapper.readValue(inputMessage.getBody(), javaType); } catch (InvalidDefinitionException ex) { throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); } catch (JsonProcessingException ex) { throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage); } }
實現 canWrite(Class<?> clazz, @Nullable MediaType mediaType)
方法,判斷是否支持往響應中寫入該類型的返回結果,以下:
@Override public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) { // 判斷是否支持該 MediaType,也就是 Content-Type if (!canWrite(mediaType)) { return false; } AtomicReference<Throwable> causeRef = new AtomicReference<>(); // 經過 ObjectMapper 判斷是否可以序列化 if (this.objectMapper.canSerialize(clazz, causeRef)) { return true; } logWarningIfNecessary(clazz, causeRef.get()); return false; }
application/json
、application/*+json
實現 writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
方法,往響應中寫入該類型的返回結果,以下:
@Override protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { // <1> 獲取編碼方式 // <1.1> 獲取 Content-Type,例如 `application/json;charset=UTF-8` MediaType contentType = outputMessage.getHeaders().getContentType(); // <1.2> 從 Content-Type 獲取編碼方式,默認 UTF8 JsonEncoding encoding = getJsonEncoding(contentType); // <2> 構建一個 Json 生成器 `generator`,指定`輸出流(響應)`和編碼 // 例如:UTF8JsonGenerator 對象(jackson-core 包) JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding); try { // <3> 設置前綴,默認沒有 writePrefix(generator, object); // <4> 得到方法的返回結果對象 `value`,返回結果類型 `javaType` Object value = object; Class<?> serializationView = null; FilterProvider filters = null; JavaType javaType = null; // <4.1> 若是返回結果對象是 MappingJacksonValue 類型,沒使用過 if (object instanceof MappingJacksonValue) { MappingJacksonValue container = (MappingJacksonValue) object; value = container.getValue(); serializationView = container.getSerializationView(); filters = container.getFilters(); } // <4.2> 獲取方法的返回結果的類型 `javaType` if (type != null && TypeUtils.isAssignable(type, value.getClass())) { javaType = getJavaType(type, null); } // <5> 建立 ObjectWriter 對象 `objectWriter`,沒有特殊配置經過 `this.objectMapper.writer()` 生成 ObjectWriter objectWriter = (serializationView != null ? this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer()); if (filters != null) { objectWriter = objectWriter.with(filters); } if (javaType != null && javaType.isContainerType()) { objectWriter = objectWriter.forType(javaType); } // <6> 獲取序列化配置 SerializationConfig config = objectWriter.getConfig(); if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) && config.isEnabled(SerializationFeature.INDENT_OUTPUT)) { objectWriter = objectWriter.with(this.ssePrettyPrinter); } // <7> **【重點】**經過 `objectWriter` 將返回結果進行序列化,設置到 `generator` 中 objectWriter.writeValue(generator, value); // <8> 設置後綴,默認沒有 writeSuffix(generator, object); // <9> 讓 `generator` 刷出數據,以 Json 格式輸出,也就是會往響應中刷出 Json 格式的返回結果 generator.flush(); } catch (InvalidDefinitionException ex) { throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); } catch (JsonProcessingException ex) { throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex); } }
獲取編碼方式
application/json;charset=UTF-8
構建一個 Json 生成器 generator
,指定輸出流(響應)
和編碼
調用writePrefix(JsonGenerator generator, Object object)
方法,設置前綴,MappingJackson2HttpMessageConverter 默認沒有
得到方法的返回結果對象 value
,返回結果類型 javaType
javaType
建立 ObjectWriter 對象 objectWriter
,沒有特殊配置經過 this.objectMapper.writer()
生成
獲取序列化配置
【重點】經過 objectWriter
將返回結果進行序列化,設置到 generator
中
調用 writeSuffix(JsonGenerator generator, Object object)
方法,設置後綴,MappingJackson2HttpMessageConverter 默認沒有
讓 generator
刷出數據,以 Json 格式輸出,也就是會往響應中刷出 Json 格式的返回結果
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
,繼承 AbstractJackson2HttpMessageConverter 抽象類,JSON 格式的消息讀取或者寫入,也就是咱們熟悉的 @RequestBody
和 @ResponseBody
註解對應的 HttpMessageConverter 消息轉換器
public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter { @Nullable private String jsonPrefix; public MappingJackson2HttpMessageConverter() { this(Jackson2ObjectMapperBuilder.json().build()); } public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) { super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json")); } public void setJsonPrefix(String jsonPrefix) { this.jsonPrefix = jsonPrefix; } public void setPrefixJson(boolean prefixJson) { this.jsonPrefix = (prefixJson ? ")]}', " : null); } @Override protected void writePrefix(JsonGenerator generator, Object object) throws IOException { if (this.jsonPrefix != null) { generator.writeRaw(this.jsonPrefix); } } }
能夠看到僅是添加了一個 jsonPrefix
屬性,JSON 的前綴,默認爲空,可是隻有前綴,沒有後綴嗎?沒搞明白😈
張小龍在談微信的本質時候說:「微信只是個平臺,消息在其中流轉」。在 Spring MVC 的 HttpMessageConverter 機制中能夠領悟到相似的道理,一次請求報文和一次響應報文,分別被抽象爲一個請求消息 HttpInputMessage 和一個響應消息 HttpOutputMessage
處理請求時,由合適的 HttpMessageConverter 消息轉換器將請求報文綁定爲方法中的形參對象,同一個對象就有可能出現多種不一樣的消息形式,好比 json 和 xml,一樣,當響應請求時,方法的返回值也一樣可能被返回爲不一樣的消息形式,好比 json 和 xml
在 Spring MVC 中,針對不一樣的消息形式,有不一樣的 HttpMessageConverter 實現類來處理各類消息形式。可是,只要這些消息所蘊含的「有效信息」是一致的,那麼各類不一樣的消息轉換器,都會生成一樣的轉換結果。至於各類消息間解析細節的不一樣,就被屏蔽在不一樣的 HttpMessageConverter 實現類中了
在 HandlerAdapter
執行 HandlerMethod
處理器的過程當中,會將該處理器封裝成 ServletInvocableHandlerMethod
對象,經過該對象來執行處理器。該對象經過反射機制調用對應的方法,在調用方法以前,藉助 HandlerMethodArgumentResolver
參數解析器從請求中獲取到對應的方法參數值,在調用方法以後,須要藉助於HandlerMethodReturnValueHandler
返回值處理器將返回結果設置到響應中,或者設置相應的 Model 和 View 用於後續的視圖渲染。在這整個過程當中須要 HttpMessageConverter 消息轉換器從請求中獲取方法入參,或者往響應中設置返回結果。
HttpMessageConverter 的實現類很是多,本文分析了咱們經常使用的兩種方法出入參格式,標註 @RequestBody
註解方法參數和標註 @ResponseBody
註解的方法
StringHttpMessageConverter
:處理 String 類型的方法入參,直接從請求體中讀取,轉換成字符串,固然也能夠往響應中寫入 String 類型的返回結果MappingJackson2HttpMessageConverter
:處理標有 @RequestBody
註解的方法參數或者返回結果,解析或者輸出 JSON 格式的數據,須要經過 ObjectMapper
和 ObjectWriter
進行反序列化和序列化等操做,也須要經過 JsonGenerator
進行 JSON 格式的消息輸出Spring MVC 默認的 JSON 消息格式的轉換器是 MappingJackson2HttpMessageConverter
這個類,不過他僅定義了一個 JSON 前綴屬性,主要的實如今其父類 AbstractJackson2HttpMessageConverter
完成的
本文對 Spring MVC 中的 HttpMessageConverter 僅作了一個淺顯的分析,對消息轉換機制有個認識就行了。至此,關於 Spring MVC 中 HandlerAdapter
組件涉及到的 HandlerAdapter
、ServletInvocableHandlerMethod
、HandlerMethodArgumentResolver
、HandlerMethodReturnValueHandler
、HttpMessageConverter
五個組件都分析完了
HandlerAdapter 真的是 Spring MVC 九大組件裏,最複雜的一個😜
參考文章:芋道源碼《精盡 Spring MVC 源碼分析》