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