这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战java
博主最近在使用注解功能时, 遇到关于Spring中关于RequestBodyAdvice使用,记录一下.mysql
/** * 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 {@code @RequestBody} or an * {@code HttpEntity} method argument. * * <p>Implementations of this contract may be registered directly with the * {@code RequestMappingHandlerAdapter} or more likely annotated with * {@code @ControllerAdvice} in which case they are auto-detected. * * @author Rossen Stoyanchev * @since 4.2 */
public interface RequestBodyAdvice {
boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, 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);
}
复制代码
如上面源码能够得知,该接口是Spring4.2版本新增的接口.查阅资料得知,该接口主要是用于给请求体参数作先后加强处理的.spring
查看一下源码中的beforeBodyRead
方法,看到RequestResponseBodyAdviceChain
类有实现该接口,并调用该方法,继续向下查看,发现AbstractMessageConverterMethodArgumentResolver
抽象类中readWithMessageConverters
方法中调用该方法.该抽象类实现了HandlerMethodArgumentResolver
处理方法参数接口.sql
...
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
// 调用接口下的实现方法
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = genericConverter.read(targetType, contextClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
}
...
复制代码
该接口方法,主要是HttpMessageConverter处理Request Body的先后作一个参数处理.markdown
AbstractMessageConverterMethodArgumentResolver
抽象类的实现子类有app
HttpEntityMethodProcessor
处理controller的方法参数是HttpEntity或RequestEntity的)/** * Resolves {@link HttpEntity} and {@link RequestEntity} method argument values * and also handles {@link HttpEntity} and {@link ResponseEntity} return values. **/
复制代码
RequestPartMethodArgumentResolver
处理方法参数是@RequestPart和MultipartFile和Part/** * Resolves the following method arguments: * <ul> * <li>Annotated with {@code @RequestPart} * <li>Of type {@link MultipartFile} in conjunction with Spring's {@link MultipartResolver} abstraction * <li>Of type {@code javax.servlet.http.Part} in conjunction with Servlet 3.0 multipart requests * </ul> **/
复制代码
RequestResponseBodyMethodProcessor
处理方法参数是RequestBody/** * Resolves method arguments annotated with {@code @RequestBody} and handles return * values from methods annotated with {@code @ResponseBody} by reading and writing * to the body of the request or response with an {@link HttpMessageConverter}. **/
复制代码
从上面可知, 在使用HandlerMethodArgumentResolver
接口时,能够对请求的参数进行解析前处理,解析后处理.且开启参数处理功能的开关在于重写的supports方法返回为True才行.ide
搭建一个能够运行的SpringBoot环境.工具
server:
port: 8081
spring:
datasource:
driverClassName: com.mysql.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/test
复制代码
@Data
public class User {
private String id;
}
复制代码
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {
@RequestMapping("/query")
public String queryById(@RequestBody User user) {
log.info("请求参数=={}", user.toString());
log.info("响应参数=={}", "id的h1样式");
return "<h1>" + user.toString() + "<h1>";
}
}
复制代码
@ControllerAdvice
@Slf4j
public class LogRequestBodyAdvice implements RequestBodyAdvice {
// 是否开启拦截 true开启 false不开启
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
/** * 请求体解析前处理 * @param httpInputMessage * @param methodParameter * @param type * @param aClass * @return * @throws IOException */
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
// 常见的业务仓场景有: 1 记录日志 2 内容加密解密 3 是否开启分页功能
log.info("拦截到的请求参数为 = {}",methodParameter.toString());
Method method=methodParameter.getMethod();
log.info("请求体读取前={}==>{}==>{}==>{}",method.getDeclaringClass().getSimpleName(),method.getName(),type.toString(),aClass.getName());
return httpInputMessage;
}
/** * 请求体解析后处理 * @param o * @param httpInputMessage * @param methodParameter * @param type * @param aClass * @return */
@Override
public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
Method method=methodParameter.getMethod();
log.info("请求体读取后={}.{}:{}",method.getDeclaringClass().getSimpleName(),method.getName(),o.toString());
return o;
}
/** * 处理没有参数 * @param o * @param httpInputMessage * @param methodParameter * @param type * @param aClass * @return */
@Override
public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
Method method=methodParameter.getMethod();
log.info("没有参数={}.{}",method.getDeclaringClass().getSimpleName(),method.getName());
return o;
}
复制代码
对于请求体参数解析前,解析后的拦截,能够更好的帮助实现业务功能,而对代码逻辑没有入侵.常见的使用场景有以下等:post
1 记录日志测试
2 内容加密解密
3 是否开启分页功能