HttpMessageConverter源码分析

基于SpringBoot 2.2.0.RELEASE

先来看下HttpMessageConverter接口的定义

public interface HttpMessageConverter<T> {

	/**
	 * 表明是否该消息转化器可以被给出的class读取
	 * @param clazz 要测试可读性的类
	 * @param mediaType 要被读取的MediaType 如果未指定 为null
	 * 通常 这个值是请求头中的content-type
	 * @return 如果可读 返回true; 否则是false
	 */
	boolean canRead(Class<?> clazz, MediaType mediaType);

	/**
	 * 表明是否该消息转化器可以写入给定的类
	 * @param clazz 要测试可写性的类
	 * @param mediaType 要被读取的MediaType 如果未指定 为null
	 * 通常 这个值是响应头中的Accept
	 * @return 如果可写 返回true; 否则是false
	 */
	boolean canWrite(Class<?> clazz, MediaType mediaType);

	/**
	 * 返回这个消息转换器可以支持的MediaType的集合
	 * @return the list of supported media types
	 */
	List<MediaType> getSupportedMediaTypes();

	/**
	 * 从给出的input message中读出给定类型的对象 然后返回 
	 * @param clazz 返回对象的类型. 这个类必须支持前面的canRead方法且返回true
	 * @param inputMessage the HTTP input message to read from
	 * @return 被转换的对象
	 * @throws IOException in case of I/O errors
	 * @throws HttpMessageNotReadableException in case of conversion errors
	 */
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;

	/**
	 * 将给定的对象写入给定的输出消息
	 * @param t 要写入输出消息的对象. 这个类型必须通过了前面的canWrite方法且返回true
	 * @param contentType 编写时使用的内容类型. 可能是{@code null}来表示必须使用转换器的默认内容类型。如	 * 果不是{@code null},则此媒体类型必须具有以前传递给这个接口的{@link #canWrite canWrite}方法 该方       法必须具有返回{@code true}。
	 * @param outputMessage the message to write to
	 * @throws IOException in case of I/O errors
	 * @throws HttpMessageNotWritableException in case of conversion errors
	 */
	void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

HttpMessageConverter接口分别有两组方法,分别对应了请求和响应。顾名思义,我们可以联想到请求入参和响应出参。没错,Spring就是根据我们请求、响应时带的MediaType来帮助我们选择合适的实现类完成我们的请求、响应的。

因为HttpMessageConverter是跟请求和响应有关的,那自然少不了RequestMappingHandlerAdapter了呀。因为这个类是实际执行mvc请求的handler方法的地方。既然是执行方法,肯定有对方法入参的解析以及对响应数据的处理。


进入到RequestMappingHandlerAdapter类,发现这个无参的构造函数有点意思,拿出来看下:

public RequestMappingHandlerAdapter() {
		this.messageConverters = new ArrayList<>(4);
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(new StringHttpMessageConverter());
		try {
			this.messageConverters.add(new SourceHttpMessageConverter<>());
		}
		catch (Error err) {
			// Ignore when no TransformerFactory implementation is available
		}
		//将AllEncompassingFormHttpMessageConverter方法标注在下面了
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
	}

static {
		ClassLoader classLoader = AllEncompassingFormHttpMessageConverter.class.getClassLoader();
		jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
						ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
		jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
		jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
		jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
	}
public AllEncompassingFormHttpMessageConverter() {
		try {
			addPartConverter(new SourceHttpMessageConverter<>());
		}
		catch (Error err) {
			// Ignore when no TransformerFactory implementation is available
		}

		if (jaxb2Present && !jackson2XmlPresent) {
			addPartConverter(new Jaxb2RootElementHttpMessageConverter());
		}

		if (jackson2Present) {
			addPartConverter(new MappingJackson2HttpMessageConverter());
		}
		else if (gsonPresent) {
			addPartConverter(new GsonHttpMessageConverter());
		}
		else if (jsonbPresent) {
			addPartConverter(new JsonbHttpMessageConverter());
		}

		if (jackson2XmlPresent) {
			addPartConverter(new MappingJackson2XmlHttpMessageConverter());
		}

		if (jackson2SmilePresent) {
			addPartConverter(new MappingJackson2SmileHttpMessageConverter());
		}
	}

这不正是初始化HttpMessageConverter实现类的地方吗?尤其是下面的方法,根据classpath下是否存在该类,而选择加载对应的HttpMessageConverter

我们点开一个实现类从类描述信息发现,其实不同的HttpMessageConverter实现类 是针对不同的MediaType的。
比如MappingJackson2HttpMessageConverter,默认情况下,该转换器支持包含UTF-8字符集的application/ jsonapplication/*+json媒体类型。

那我们就拿我们最常用的基于application/jsonMappingJackson2HttpMessageConverter来分析下:

public MappingJackson2HttpMessageConverter() {
//无参的构造函数调用有参的构造函数
		this(Jackson2ObjectMapperBuilder.json().build());
	}

	public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {		
	   //将applicat/json 这个媒体类型传递给父类AbstractJackson2HttpMessageConverter中supportedMediaTypes
		super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
	}

AbstractJackson2HttpMessageConverter

先分析下read

@Override
	public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
		return canRead(clazz, null, mediaType);
	}

	@Override
	public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
    //这里调用AbstractHttpMessageConverter#canRead
    //大意是说如果mediaType为空 返回true
    //否则拿supportedMediaTypes与mediaType比较,
    //如果supportedMediaTypes包含了mediaType则返回true
		if (!canRead(mediaType)) {
			return false;
		}
		//为指定类型和上下文返回Jackson
		//个人理解 这个JavaType就包含了待入参处理参数的类型
		JavaType javaType = getJavaType(type, contextClass);
		AtomicReference<Throwable> causeRef = new AtomicReference<>();
		//如果objectMapper可以反序列化javaType 那么就可读
		if (this.objectMapper.canDeserialize(javaType, causeRef)) {
			return true;
		}
		logWarningIfNecessary(javaType, causeRef.get());
		return false;
	}
	@Override
	public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

		JavaType javaType = getJavaType(type, contextClass);
		return readJavaType(javaType, inputMessage);
	}

 	  @Override
	protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

		JavaType javaType = getJavaType(clazz, null);
		return readJavaType(javaType, inputMessage);
	}



	private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
		try {
		//如果请求的HttpInputMessage报文属于MappingJacksonInputMessage这个类
			if (inputMessage instanceof MappingJacksonInputMessage) {
				Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
				if (deserializationView != null) {
				//最终这里 就和我们熟知的json反序列化一致了。
				//至此 application/json 入参转为对象完成
					return this.objectMapper.readerWithView(deserializationView).forType(javaType).
							readValue(inputMessage.getBody());
				}
			}
			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);
		}
	}

有个read相关操作的铺垫,那write操作应该就更好分析了,这里就简单地看下write方法吧。

@Override
	protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {

		MediaType contentType = outputMessage.getHeaders().getContentType();
		JsonEncoding encoding = getJsonEncoding(contentType);
		JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
		try {
			writePrefix(generator, object);

			Object value = object;
			Class<?> serializationView = null;
			FilterProvider filters = null;
			JavaType javaType = null;

			if (object instanceof MappingJacksonValue) {
				MappingJacksonValue container = (MappingJacksonValue) object;
				value = container.getValue();
				serializationView = container.getSerializationView();
				filters = container.getFilters();
			}
			if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
				javaType = getJavaType(type, null);
			}
			//这里就是将响应对象序列化的地方,简单的讲 跟我们平时json序列化一样
			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);
			}
			SerializationConfig config = objectWriter.getConfig();
			if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
					config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
				objectWriter = objectWriter.with(this.ssePrettyPrinter);
			}
			objectWriter.writeValue(generator, value);

			writeSuffix(generator, object);
			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);
		}
	}

从我的角度出发,HttpMessageConverter在框架里的调用是跟content_type息息相关的。而分析HttpMessageConverter中方法的运行流程,也大同小异:先判断是否可读/可写,如果可以,在读/写成content_type希望的格式。


下面列出了常用HttpMessageConverter与媒体类型的对应信息:

HttpMessageConverter处理哪些格式的数据
StringHttpMessageConverter读取字符串格式的数据和写出二进制格式的数据
ByteArrayHttpMessageConverter读取二进制格式的数据和写出二进制格式的数据
FormHttpMessageConverter负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据
ResourceHttpMessageConverter负责读取资源文件和写出资源文件数据
Jaxb2RootElementHttpMessageConverter读取和写入xml 标签格式的数据
ResourceHttpMessageConverter负责读取资源文件和写出资源文件数据
MappingJacksonHttpMessageConverter读取和写入json格式的数据

至于HttpMessageConverter在Spring框架中是如何使用的?会在分析HandlerMethodArgumentResolver时一并分析的。因为在解析@RequstBody方法参数的解析少不了HttpMessageConverter的参与。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值