RequestBodyAdvice 和 ResponseBodyAdvice 原理
RequestBodyAdvice
RequestBodyAdvice是 Spring 框架中的一个接口,用于在读取请求体之前(或读取得到的对象作为参数传入 @RequestBody 或 HttpEntity Controller 方法之前)进行自定义处理。
RequestBodyAdvice (Spring Framework 6.2.8 API):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.
下面是一个简单的示例:
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import java.io.IOException;
import java.lang.reflect.Type;
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
@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 {
// 在请求体被读取之前,可以对输入流进行处理
// 例如:记录日志、解密等
return inputMessage; // 返回处理后的输入流
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 请求体被读取之后,可以对其进行处理
// 例如:修改请求体内容
return body; // 返回处理后的请求体
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 处理空请求体的情况
return body; // 返回处理后的请求体
}
}
先在CustomRequestBodyAdvice的各个方法中添加断点,通过调用下面的接口来进行测试:
import com.source.mybatis.entity.User;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getDetail")
public User getDetail(@RequestBody User user) {
return user;
}
}
首先进入的是supports方法:
![]() |
![]() |
从InvocableHandlerMethod#invokeForRequest开始看,其调用getMethodArgumentValues获取参数:
![]() |
getMethodArgumentValues方法遍历各个方法参数,调用HandlerMethodArgumentResolverComposite#resolveArgument逐个处理:
![]() |
该方法先调用HandlerMethodArgumentResolverComposite#getArgumentResolver获取到参数对应的HandlerMethodArgumentResolver,然后调用HandlerMethodArgumentResolver#resolveArgument获得参数值:
![]() |
查看HandlerMethodArgumentResolverComposite#getArgumentResolver可以看到其遍历argumentResolvers列表,返回支持处理该参数的第一个HandlerMethodArgumentResolver实例:
![]() |
因为当前 Controller 方法参数被@RequestBody标注,而RequestResponseBodyMethodProcessor的supportsParameter对改情况返回 true,所以最终处理参数的是RequestResponseBodyMethodProcessor实例:
![]() |
![]() |
继续看RequestResponseBodyMethodProcessor#resolveArgument如何解析参数,可以看到其调用readWithMessageConverters读取参数:
![]() |
readWithMessageConverters方法内部调用另一个readWithMessageConverters:
![]() |
readWithMessageConverters内部调用getAdvice获取切面,然后调用切面的beforeBodyRead方法:
![]() |
beforeBodyRead方法内遍历各个RequestBodyAdvice,调用其supports方法:
![]() |
这里也就是CustomRequestBodyAdvice#supports被调用到的点。supports返回 true,所以下面马上调用到CustomRequestBodyAdvice#beforeBodyRead:
![]() |
执行完beforeBodyRead之后,下一步就是执行afterBodyRead,类似地也是获取切面,调用其support方法,返回 true 则执行afterBodyRead:
![]() |
可以看到调用afterBodyRead方法时已经完成了参数解析:
![]() |
afterBodyRead方法调用后,才进入到 Controller 方法:
![]() |
ResponseBodyAdvice
ResponseBodyAdvice是另一个对应的常见接口,用于在执行完@ResponseBody或ResponseEntity控制器方法后,响应体被处理之前对 Controller 方法值进行自定义处理。
ResponseBodyAdvice (Spring Framework 6.2.8 API):Allows customizing the response after the execution of an @ResponseBody or a ResponseEntity controller method but before the body is written with an HttpMessageConverter.
下面是一个简单的示例:
import com.source.mybatis.entity.User;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
// 这里可以根据需要判断是否支持特定的响应
return true; // 支持所有响应
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
// 在响应体写入之前,可以对其进行处理
// 例如:添加额外的字段、修改数据格式等
if (body instanceof User) {
User user = (User) body;
user.setName(user.getName() + " (处理后)");
}
return body; // 返回处理后的响应体
}
}
同样还是在CustomResponseBodyAdvice中添加断点,调用前面的接口调试。
首先调用到的是supports方法:
![]() |
![]() |
先从ServletInvocableHandlerMethod#invokeAndHandle看起,可以看到其先调用前面提到的InvocableHandlerMethod#invokeForRequest得到 Controller 方法返回值,再调用HandlerMethodReturnValueHandlerComposite#handleReturnValue处理返回值:
![]() |
HandlerMethodReturnValueHandlerComposite#handleReturnValue方法内先调用selectHandler获取处理返回值的HandlerMethodReturnValueHandler实例,再调用其handleReturnValue方法,而且可以看到这里获取到的是RequestResponseBodyMethodProcessor实例,且returnValue即 Controller 方法的返回值:
![]() |
进入RequestResponseBodyMethodProcessor#handleReturnValue,其调用AbstractMessageConverterMethodProcessor#writeWithMessageConverters方法:
![]() |
writeWithMessageConverters方法内,也是调用getAdvice获取切面,然后调用切面的beforeBodyWrite方法:
![]() |
具体会调用到RequestResponseBodyAdviceChain#beforeBodyWrite方法,方法内部调用RequestResponseBodyAdviceChain#processBody方法:
![]() |
RequestResponseBodyAdviceChain#processBody方法内部,遍历ResponseBodyAdvice切面,调用切面的support方法,这里也就是断点的入口:
![]() |
如果support方法返回 true,则调用beforeBodyWrite方法,所以断点马上会进入beforeBodyWrite:
![]() |
![]() |
如此由ResponseBodyAdvice处理完返回值之后,才会写入请求:
![]() |



























浙公网安备 33010602011771号