SpringFramework之RequestBodyAdvice

    Spring版本5.1.4.release.

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

    List-1

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

public interface HandlerMethodArgumentResolver {

    boolean supportsParameter(MethodParameter var1);

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

    List-3

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

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

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特性来处理业务问题.