前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringFramework之RequestBodyAdvice

SpringFramework之RequestBodyAdvice

作者头像
克虏伯
发布2020-07-01 15:19:54
9490
发布2020-07-01 15:19:54
举报
文章被收录于专栏:软件开发-青出于蓝

  Spring版本5.1.4.release.

    本来RequestBodyAdvice和ResponseBodyAdvice是成对一起的,这里先分析RequestBodyAdvice.

List-1

代码语言:javascript
复制
public interface RequestBodyAdvice {

	boolean supports(MethodParameter methodParameter, Type targetType,
			Class<? extends HttpMessageConverter<?>> converterType);

	HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;

	Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType);

	@Nullable
	Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType);

}

    如List-1所示, 看到supports方法就应该想到使用到了模板设计模式, 那么在哪使用到的呢?

    RequestBodyAdvice,只有在@RequestBody注解的方法时才有效,为什么呢? 

    一切的一切要从RequestResponseBodyMethodProcessor类上说起,如下图1,实现了HandlerMethodArgumentResolver

                                                                           图1

    RequestResponseBodyMethodProcessor类实现了HandlerMethodArgumentResolver接口, 使用到@RequestBody注解才会让RequestBodyAdvice生效就是这里导致的,如下List-2,只有supportsParameter返回true,调用调用resolveArgument方法.

List-2

代码语言:javascript
复制
public interface HandlerMethodArgumentResolver {

    boolean supportsParameter(MethodParameter var1);

    @Nullable
    Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}

List-3

代码语言:javascript
复制
RequestResponseBodyMethodProcessor{
  ...

  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestBody.class);
  }


  @Override
  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    ...
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    
    ...

    return adaptArgumentIfNecessary(arg, parameter);
  }

...
}

    如上List-3,RequestResponseBodyMethodProcessor的supportsParameter方法中判断方法上是否有RequestBody注解,所以如果方法上没有此注解,那么RequestResponseBodyMethodProcessor的resolveArgument方法就不会被调用,而调用RequestBodyAdice的代码就在resolveArgument方法的readWithMessageConverters中。

    RequestResponseBodyMethodProcessor.readWithMessageConverters()会间接调用父类AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters方法,如下List-4

List-4

代码语言:javascript
复制
AbstractMessageConverterMethodArgumentResolver{
...
	protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

      ...
		EmptyBodyCheckingHttpInputMessage message;
		try {
			message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
          //1
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
				GenericHttpMessageConverter<?> genericConverter =
						(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
				if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
						(targetClass != null && converter.canRead(targetClass, contentType))) {
					if (message.hasBody()) {
                   //2
						HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
						body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
								((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
						//3
                   body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
					}
					else {
                   //4
						body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
					}
					break;
				}
			}
		}
    ...
		return body;
	}

...
}
  1. 遍历HttpMessageConverter,找到能处理解析请求参数的Converter
  2. 在读取body前,调用RequestBodyAdvice的beforeBodyRead
  3. 在读取body后,调用RequestBodyAdvice的afterBodyRead
  4. 如果请求的消息体为空,则调用RequestBodyAdvice的handleEmptyBody

    AbstractMessageConverterMethodArgumentResolver中getAdvice方法返回的是RequestResponseBodyAdviceChain,看类名就知道使用到了链式设计模式。如下List-5,方法before/afterBodyRead中遍历requstBodyAdvice,逐个调用RequestBodyAdvice的supports方法,如果返回true则再调用before/afterBodyAdvice.

List-5

代码语言:javascript
复制
class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice<Object> {

	private final List<Object> requestBodyAdvice = new ArrayList<>(4);

	private final List<Object> responseBodyAdvice = new ArrayList<>(4);

  ...

	@Override
	public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {

		for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
			if (advice.supports(parameter, targetType, converterType)) {
				request = advice.beforeBodyRead(request, parameter, targetType, converterType);
			}
		}
		return request;
	}

	@Override
	public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

		for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
			if (advice.supports(parameter, targetType, converterType)) {
				body = advice.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
			}
		}
		return body;
	}

	@Override
	@Nullable
	public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

		for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
			if (advice.supports(parameter, targetType, converterType)) {
				body = advice.handleEmptyBody(body, inputMessage, parameter, targetType, converterType);
			}
		}
		return body;
	}

...

}

    到此,我们知道了RequestBodyAdvice可以修改spring解析出的参数,而后再传入到controller类的方法上,后续我会讲解如何使用RequestBodyAdvice特性来处理业务问题.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档