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.ServletRequest 和 javax.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 MVC 源码分析 - HandlerAdapter 组件(五)之 HttpMessageConverter_java

  • Spring Boot

    Spring MVC 源码分析 - HandlerAdapter 组件(五)之 HttpMessageConverter_java_02

示例


@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 既是参数解析器,也是返回结果处理器

总结下来,整个过程如下所示:

Spring MVC 源码分析 - HandlerAdapter 组件(五)之 HttpMessageConverter_返回结果_03

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) 抽象方法
  1. 刷出流

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
  1. 构建一个 Json 生成器 generator,指定输出流(响应)和编码
  2. 调用writePrefix(JsonGenerator generator, Object object)方法,设置前缀,MappingJackson2HttpMessageConverter 默认没有
  3. 获得方法的返回结果对象 value,返回结果类型 javaType
  1. 如果返回结果对象是 MappingJacksonValue 类型,则从该对象中相关属性中获取,没使用过😈
  2. 获取方法的返回结果的类型 javaType
  1. 创建 ObjectWriter 对象 objectWriter,没有特殊配置通过 this.objectMapper.writer() 生成
  2. 获取序列化配置
  3. 【重点】通过 objectWriter 将返回结果进行序列化,设置到 generator 中
  4. 调用 writeSuffix(JsonGenerator generator, Object object) 方法,设置后缀,MappingJackson2HttpMessageConverter 默认没有
  5. 让 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 格式的数据,需要通过 ObjectMapper 和 ObjectWriter 进行反序列化和序列化等操作,也需要通过 JsonGenerator 进行 JSON 格式的消息输出

Spring MVC 默认的 JSON 消息格式的转换器是 MappingJackson2HttpMessageConverter 这个类,不过他仅定义了一个 JSON 前缀属性,主要的实现在其父类 AbstractJackson2HttpMessageConverter 完成的

本文对 Spring MVC 中的 HttpMessageConverter 仅做了一个浅显的分析,对消息转换机制有个认识就好了。至此,关于 Spring MVC 中 HandlerAdapter 组件涉及到的 HandlerAdapter 、ServletInvocableHandlerMethodHandlerMethodArgumentResolverHandlerMethodReturnValueHandlerHttpMessageConverter 五个组件都分析完了

HandlerAdapter 真的是 Spring MVC 九大组件里,最复杂的一个😜