精尽Spring MVC源码分析 - HandlerAdapter 组件(五)之 HttpMessageConverter

该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读

Spring 版本:5.1.14.RELEASE

该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》

HandlerAdapter 组件

HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器

由于 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:

HandlerAdapter 组件(五)之 HttpMessageConverter

本文是接着《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》一文来分享 HttpMessageConverter 组件。在 HandlerAdapter 执行处理器的过程中,具体的执行过程交由 ServletInvocableHandlerMethod 对象来完成,其中需要先通过 HandlerMethodArgumentResolver 参数解析器从请求中解析出方法的入参,然后再通过反射机制调用对应的方法,获取到执行结果后需要通过 HandlerMethodReturnValueHandler 结果处理器来进行处理。在处理返回结果的过程中,可能需要通过 HttpMessageConverter 消息转换器将返回结果设置到响应体中,当然也可能需要通过它从请求体获取入参。

在使用 Spring MVC 时,@RequestBody@ResponseBody 两个注解,分别完成请求报文到 Java 对象Java 对象到响应报文的转换,底层的实现就是通过 Spring 3.x 中引入的 HttpMessageConverter 消息转换机制来实现的。

再开始阅读本文之前,先来理解一些概念。在处理 HTTP 请求的过程中,需要解析请求体,返回结果设置到响应体。在 Servlet 标准中,javax.servlet.ServletRequestjavax.servlet.ServletResponse 分别有有以下方法:

// 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 源码分析》

posted @ 2020-12-21 15:13  月圆吖  阅读(195)  评论(0编辑  收藏