HttpMessageConverter逻辑梳理

本文目录结构:
一、快速了解springHttpMessageConverter机制

二、结合@RequestBody解读HttpMessageConverter解析请求报文机制

三、结合@ResponseBody解读HttpMessageConverter解析响应报文机制

四、创建自定义HttpMessageConverter实现参数加解密


一、快速了解springHttpMessageConverter机制
快速过一遍相关代码(这里只摘取核心的代码,并注释方法作用,其余代码使用【…】省略,感兴趣的话可以自行查看源码)

	org.springframework.web.method.support.InvocableHandlerMethod
	
	/**
	 * 该方法先解析request的请求参数,然后调用具体方法
	 * 1.getMethodArgumentValues 解析请求参数
	 * 2.doInvoke调用具体方法
	*/
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		return doInvoke(args);
	}
	/**
	 * 解析请求参数
	 * 我们知道http content-type有很多种类型,
	 * 譬如:text/html; text/xml; application/json; application/x-www-form-urlencoded等(我们也可以定义自己的content-type),
	 * 不同的content-type对于请求参数的数据结构是不尽相同的,因此spring提供了一些内置的methodArgumentResolver以及messageConverter来处理常见的content-type
	*/
	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		......
		
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			......
			// 根据请求参数类型判断是否有适配的methodArgumentResolver
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				// 根据请求参数类型选择对应的methodArgumentResolver,然后解析参数
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				......
			}
		}
		return args;
	}
	
	org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
	/**
	 * 根据请求参数类型判断是否有适配的methodArgumentResolver
	*/
	public boolean supportsParameter(MethodParameter parameter) {
		return getArgumentResolver(parameter) != null;
	}
	
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

	/**
	 * 根据请求参数类型选择对应的methodArgumentResolver,然后解析参数
	*/
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
		// 根据匹配到的methodArgumentResolver,去methodArgumentResolver内去实现具体的参数解析
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}

其实核心流程就是根据parameter去匹配resolver,然后将具体的参数解析工作交给匹配到的resolver,resolver再将解析工作委托给converter

二、结合@RequestBody解读HttpMessageConverter解析请求报文机制

在这里插入图片描述

@RequestBody时序图【借助时序图遍历理清调用逻辑】

  1. 根据parameter匹配resolver
    通过阅读第一节核心代码,我们可以知道spring是通过getArgumentResolver方法去获取对应的resolver,具体的逻辑就是遍历所有的resolver实现类(这里包括自定义的resolver),然后通过supportsParameter方法判断是否匹配,如果匹配则返回(说明resolver有优先级,越靠前优先级越高
    通过debug发现,@RequestBody最终匹配到RequestResponseBodyMethodProcessor类。这一点可以通过该类中的supportsParameter方法得到证实
	org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
	
	/**
	 * 指定该resolver支持的参数类型
	 * 可以看到该方法判断parameter是否包含@ReqeustBody注解,如果包含则支持,否则不支持
	*/
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}
  1. 根据parameter匹配converter
    通过debug发现,@RequestBody最终匹配到AbstractJackson2HttpMessageConverter类。这一点可以通过该类中的canRead方法得到证实 ,该方法主要是判断当前converter中预设的content-type是否包含request header中的content-type
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
		if (!canRead(mediaType)) {
			return false;
		}
		JavaType javaType = getJavaType(type, contextClass);
		ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), mediaType);
		if (objectMapper == null) {
			return false;
		}
		AtomicReference<Throwable> causeRef = new AtomicReference<>();
		if (objectMapper.canDeserialize(javaType, causeRef)) {
			return true;
		}
		logWarningIfNecessary(javaType, causeRef.get());
		return false;
	}

3.converter解析参数
上一步中我们知道匹配到的converter是AbstractJackson2HttpMessageConverter,具体的解析方法是readJavaType,其实就是将json转换为实体类然后返回,至此就完成了@RequestBody类型参数的解析

	/**
	 * 解析参数
	*/
	public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

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

	/**
	 * 将inputMessage中的数据流转换为对应的实体类
	*/
	private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
		MediaType contentType = inputMessage.getHeaders().getContentType();
		Charset charset = getCharset(contentType);

		ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
		Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);

		boolean isUnicode = ENCODINGS.containsKey(charset.name()) ||
				"UTF-16".equals(charset.name()) ||
				"UTF-32".equals(charset.name());
		try {
			if (inputMessage instanceof MappingJacksonInputMessage) {
				Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
				if (deserializationView != null) {
					ObjectReader objectReader = objectMapper.readerWithView(deserializationView).forType(javaType);
					if (isUnicode) {
						return objectReader.readValue(inputMessage.getBody());
					}
					else {
						Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
						return objectReader.readValue(reader);
					}
				}
			}
			if (isUnicode) {
				return objectMapper.readValue(inputMessage.getBody(), javaType);
			}
			else {
				Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
				return objectMapper.readValue(reader, 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);
		}
	}

三、结合@ResponseBody解读HttpMessageConverter解析响应报文机制
在这里插入图片描述

@ResponseBody时序图【借助时序图遍历理清调用逻辑】

该流程核心逻辑和@RequestBody逻辑类似,只是由read转write,这里就不展开了。

四、创建自定义HttpMessageConverter实现参数加解密
下一篇【自定义HttpMessageConverter实现参数加解密

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值