精盡Spring MVC源碼分析 - HandlerAdapter 組件(五)之 HttpMessageConverter

該系列文檔是本人在學習 Spring MVC 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀html

Spring 版本:5.2.4.RELEASEjava

該系列其餘文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》git

HandlerAdapter 組件

HandlerAdapter 組件,處理器的適配器。由於處理器 handler 的類型是 Object 類型,須要有一個調用者來實現 handler 是怎麼被執行。Spring 中的處理器的實現多變,好比用戶的處理器能夠實現 Controller 接口或者 HttpRequestHandler 接口,也能夠用 @RequestMapping 註解將方法做爲一個處理器等,這就致使 Spring MVC 沒法直接執行這個處理器。因此這裏須要一個處理器適配器,由它去執行處理器github

因爲 HandlerMapping 組件涉及到的內容較多,考慮到內容的排版,因此將這部份內容拆分紅了五個模塊,依次進行分析:web

HandlerAdapter 組件(五)之 HttpMessageConverter

本文是接着《HandlerAdapter 組件(四)之 HandlerMethodReturnValueHandler》一文來分享 HttpMessageConverter 組件。在 HandlerAdapter 執行處理器的過程當中,具體的執行過程交由 ServletInvocableHandlerMethod 對象來完成,其中須要先經過 HandlerMethodArgumentResolver 參數解析器從請求中解析出方法的入參,而後再經過反射機制調用對應的方法,獲取到執行結果後須要經過 HandlerMethodReturnValueHandler 結果處理器來進行處理。在處理返回結果的過程當中,可能須要經過 HttpMessageConverter 消息轉換器將返回結果設置到響應體中,固然也可能須要經過它從請求體獲取入參。spring

在使用 Spring MVC 時,@RequestBody@ResponseBody 兩個註解,分別完成請求報文到 Java 對象Java 對象到響應報文的轉換,底層的實現就是經過 Spring 3.x 中引入的 HttpMessageConverter 消息轉換機制來實現的。編程

再開始閱讀本文以前,先來理解一些概念。在處理 HTTP 請求的過程當中,須要解析請求體,返回結果設置到響應體。在 Servlet 標準中,javax.servlet.ServletRequestjavax.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 方法微信

handleReturnValue

// 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 對象

  • ServletServerHttpRequest:實現了 ServerHttpRequest、HttpRequest、HttpInputMessage、HttpMessage接口
  • ServletServerHttpResponse:實現 ServerHttpResponse、HttpOutputMessage 接口

上面這些接口定義了一些獲取請求和設置響應相關信息的方法,便於獲取請求和設置響應

writeWithMessageConverters

// 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 方法,進行寫入

HttpInputMessage 接口

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 的消息轉換器所做用的受體請求消息的內部抽象,消息轉換器從請求消息中按照規則提取消息,轉換爲方法形參中聲明的對象。

HttpOutputMessage 接口

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 的消息轉換器所做用的受體響應消息的內部抽象,消息轉換器將響應消息按照必定的規則寫到響應報文中

HttpMessageConverter 接口

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 這個類,往響應體中寫數據,其中會經過 MappingJackson2HttpMessageConverterList<User> 返回結果寫入響應。

提示:RequestResponseBodyMethodProcessor 既是參數解析器,也是返回結果處理器

總結下來,整個過程以下所示:

AbstractHttpMessageConverter

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

實現 getSupportedMediaTypes() 方法,得到支持的 MediaType,以下:

@Override
public List<MediaType> getSupportedMediaTypes() {
    return Collections.unmodifiableList(this.supportedMediaTypes);
}

canRead

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

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

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

實現 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;
  1. 獲取響應頭

  2. 若是 Content-Type 爲空則設置默認的

  3. 往響應中寫入數據

    1. 若是是流,則再封裝一層,StreamingHttpOutputMessage 對象
    2. 普通對象,則直接調用 writeInternal(T t, HttpOutputMessage outputMessage) 抽象方法
  4. 刷出流

StringHttpMessageConverter

org.springframework.http.converter.StringHttpMessageConverter,繼承 AbstractHttpMessageConverter 抽象類,String 類型的消息轉換器

supports

實現 supports(Class<?> clazz) 方法,是否支持從請求中讀取該類型的方法參數,或者是否支持往響應中寫入該類型的返回結果,以下:

@Override
public boolean supports(Class<?> clazz) {
    return String.class == clazz;
}

String 類就能夠,因此在示例中,會使用 StringHttpMessageConverter 消息轉換器

readInternal

實現 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 流中讀取出來,轉換成字符串

AbstractJackson2HttpMessageConverter

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

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

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

實現 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;
}
  1. 判斷是否支持該 MediaType,也就是 Content-Type,支持 application/jsonapplication/*+json
  2. 經過 ObjectMapper 判斷是否可以序列化

writeInternal

實現 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);
    }
}
  1. 獲取編碼方式

    1. 獲取 Content-Type,例如 application/json;charset=UTF-8
    2. 從 Content-Type 獲取編碼方式,默認 UTF8
  2. 構建一個 Json 生成器 generator,指定輸出流(響應)和編碼

  3. 調用writePrefix(JsonGenerator generator, Object object)方法,設置前綴,MappingJackson2HttpMessageConverter 默認沒有

  4. 得到方法的返回結果對象 value,返回結果類型 javaType

    1. 若是返回結果對象是 MappingJacksonValue 類型,則從該對象中相關屬性中獲取,沒使用過😈
    2. 獲取方法的返回結果的類型 javaType
  5. 建立 ObjectWriter 對象 objectWriter,沒有特殊配置經過 this.objectMapper.writer() 生成

  6. 獲取序列化配置

  7. 【重點】經過 objectWriter 將返回結果進行序列化,設置到 generator

  8. 調用 writeSuffix(JsonGenerator generator, Object object) 方法,設置後綴,MappingJackson2HttpMessageConverter 默認沒有

  9. generator 刷出數據,以 Json 格式輸出,也就是會往響應中刷出 Json 格式的返回結果

MappingJackson2HttpMessageConverter

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 格式的數據,須要經過 ObjectMapperObjectWriter 進行反序列化和序列化等操做,也須要經過 JsonGenerator 進行 JSON 格式的消息輸出

Spring MVC 默認的 JSON 消息格式的轉換器是 MappingJackson2HttpMessageConverter 這個類,不過他僅定義了一個 JSON 前綴屬性,主要的實如今其父類 AbstractJackson2HttpMessageConverter 完成的

本文對 Spring MVC 中的 HttpMessageConverter 僅作了一個淺顯的分析,對消息轉換機制有個認識就行了。至此,關於 Spring MVC 中 HandlerAdapter 組件涉及到的 HandlerAdapterServletInvocableHandlerMethodHandlerMethodArgumentResolverHandlerMethodReturnValueHandlerHttpMessageConverter 五個組件都分析完了

HandlerAdapter 真的是 Spring MVC 九大組件裏,最複雜的一個😜

參考文章:芋道源碼《精盡 Spring MVC 源碼分析》

相關文章
相關標籤/搜索