Author: lwyang
SpringBoot:2.1.9.RELEASE
概述
RequestBodyAdvice
Allows customizing the request before its body is read and converted into an Object and also allows for processing of the resulting Object before it is passed into a controller method as an @RequestBody or an HttpEntity method argument.
Implementations of this contract may be registered directly with the RequestMappingHandlerAdapter or more likely annotated with @ControllerAdvice in which case they are auto-detected.
实现RequestBodyAdvice
接口可以在请求体被HttpMessageConverter
转换成Object前、后注入自己的逻辑,该方法的参数需要是被 @RequestBody
修饰或者HttpEntity
,实现RequestBodyAdvice
接口的类需要使用@ControllerAdvice
注册进框架中
ResponseBodyAdvice
Allows customizing the response after the execution of an @ResponseBody or a ResponseEntity controller method but before the body is written with an HttpMessageConverter.
Implementations may be registered directly with RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver or more likely annotated with @ControllerAdvice in which case they will be auto-detected by both.
实现ResponseBodyAdvice
接口可以在方法的返回值被HttpMessageConverter
写入之前自定义包装方法返回值,比如进行统一的返回值处理,实现ResponseBodyAdvice
的接口同样需要使用@ControllerAdvice
注册进框架中
HttpMessageConverter
Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
HttpMessageConverter
可以将请求转换为指定的对象或将指定的对象转换为对应的响应,我们常用的Json格式的转换对应的实现类是MappingJackson2HttpMessageConverter
分析
当请求到达HandlerMethodArgumentResolver
后,会通过supportsParameter
方法来判断应当使用哪一种HandlerMethodArgumentResolver
实现类
// class HandlerMethodArgumentResolverComposite
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
// 调用supportsParameter 方法判断使用哪一种实现类
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
如果使用@RequestBody
、@ResponseBody
注解,可以看到在RequestResponseBodyMethodProcessor
该实现类的supportsParameter
方法里,会去判断该方法参数是否使用了RequestBody
注解
// class RequestResponseBodyMethodProcessor ... implements HandlerMethodArgumentResolver
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
通过上述流程就可以找到能够处理有RequestBody
注解的类,即为RequestResponseBodyMethodProcessor
。同理也是能够处理ResponseBody
注解的类
//class RequestResponseBodyMethodProcessor ... implements HandlerMethodReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
接下来进行参数解析时它会把请求体中Json格式的数据转换成对应的实体类
class RequestResponseBodyMethodProcessor ... implements HandlerMethodArgumentResolver
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
//通过HttpMessageConverter对请求体进行解析
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
//这里对参数进行校验,处理@Validated、@Valid注解
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
【彩蛋】:在上面这个方法中(RequestResponseBodyMethodProcessor#resolveArgument
),还发现了处理参数检验的方法,就是我们常用的@Validated
、@Valid
注解修饰的参数
在readWithMessageConverters
方法中,最终会去调用父类的重载方法:
//abstract class AbstractMessageConverterMethodArgumentResolver
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
......
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse)
:((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
}
return body;
genericConverter.read()
是真正HttpMessageConverter
的转换方法,在这个方法前后会去调用beforeBodyRead
、afterBodyRead
,也就是实现RequestBodyAdvice
接口重写的方法
getAdvice()
方法会返回一个RequestResponseBodyAdviceChain
// class RequestResponseBodyAdviceChain
class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice<Object> {
private final List<Object> requestBodyAdvice = new ArrayList<>(4);
private final List<Object> responseBodyAdvice = new ArrayList<>(4);
......
这个类中的两个List就分别装着实现了requestBodyAdvice 和responseBodyAdvice 接口的Bean,那么问题来了,这两个List中的Bean时什么时候被加进去的呢???
【Question Start】
在RequestMappingHandlerAdapter
类中有个initControllerAdviceCache方法
// class RequestMappingHandlerAdapter
private void initControllerAdviceCache() {
// 获取所有带有@ControllerAdvice注解的Bean
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
// 判断如果该Bean实现了requestBodyAdvice 或者responseBodyAdvice 接口,就存起来
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
......
然后在getDefaultArgumentResolvers
方法里会把保存的这个List(requestResponseBodyAdvice
)传给RequestResponseBodyMethodProcessor
,这样就回到了上面的说的流程中了
//class RequestMappingHandlerAdapter
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
//把this.requestResponseBodyAdvice 传给RequestResponseBodyMethodProcessor
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
......
RequestResponseBodyMethodProcessor
通过构造函数接收到这个List后会调用父类(AbstractMessageConverterMethodArgumentResolver
)的构造函数保存下来
//class AbstractMessageConverterMethodArgumentResolver
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
@Nullable List<Object> requestResponseBodyAdvice) {
this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
}
这里就通过RequestResponseBodyAdviceChain
的构造函数将这个List传进去后,分别填充刚刚上面说的requestBodyAdvice和responseBodyAdvice两个List
// class RequestResponseBodyAdviceChain
public RequestResponseBodyAdviceChain(@Nullable List<Object> requestResponseBodyAdvice) {
this.requestBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, RequestBodyAdvice.class));
this.responseBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, ResponseBodyAdvice.class));
}
这样就回答了刚刚上面的这个问题了!
【Question END】
在请求返回(Json格式)的时候,RequestResponseBodyMethodProcessor
类会去调用handleReturnValue
方法
// RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
接下来会去调用父类AbstractMessageConverterMethodProcessor
的writeWithMessageConverters
方法
// AbstractMessageConverterMethodProcessor implements HandlerMethodReturnValueHandler
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
......
genericConverter.write
是HttpMessageConverter
的转换(转Json)方法,在转换之前会先去调用beforeBodyWrite
,也就是实现ResponseBodyAdvice
接口重写的方法
测试
接下来用代码测试下上述分析是否正确:
@RestController
@ControllerAdvice
@RequestMapping("/")
public class MyTest implements RequestBodyAdvice, ResponseBodyAdvice {
@GetMapping("test")
public List adviceOrder(@RequestBody Person person){
System.out.println("===adviceOrder===");
System.out.println("name: "+person.getName()+" age: "+person.getAge());
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(person.getName());
arrayList.add(person.getAge());
return arrayList;
}
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
System.out.println("RequestBodyAdvice beforeBodyRead");
return inputMessage;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
System.out.println("RequestBodyAdvice afterBodyRead");
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return null;
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
System.out.println("ResponseBodyAdvice beforeBodyWrite");
Map<String, Object> map = new HashMap<>();
map.put("data", body);
Result result = new Result();
result.setCode("200");
result.setMsg("OK");
result.setData(map);
return result;
}
}
然后自己写个HttpMessageConverter
继承MappingJackson2HttpMessageConverter
,用来打印转换的执行过程
@Component
public class MyConverter extends MappingJackson2HttpMessageConverter {
@Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
System.out.println("MappingJackson2HttpMessageConverter read");
return super.read(type, contextClass, inputMessage);
}
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
System.out.println("MappingJackson2HttpMessageConverter write");
super.writeInternal(object, type, outputMessage);
}
}
【用postman测试结果】
{
"code": "200",
"msg": "OK",
"data": {
"data": [
"lwyang",
"10"
]
}
}
【Consloe打印结果】
RequestBodyAdvice beforeBodyRead
MappingJackson2HttpMessageConverter read
RequestBodyAdvice afterBodyRead
===adviceOrder===
name: lwyang age: 10
ResponseBodyAdvice beforeBodyWrite
MappingJackson2HttpMessageConverter write
可以看到执行顺序和上面分析的顺序一致
RequestBodyAdvice beforeBodyRead
==》 HttpMessageConverter
==》RequestBodyAdvice afterBodyRead
==》业务Method ==》ResponseBodyAdvice beforeBodyWrite
==》HttpMessageConverter