套缈 发表于 2025-5-30 10:54:58

ResponseBodyAdvice与@RestControllerAdvice

ResponseBodyAdvice与@RestControllerAdvice


1. @RestControllerAdivce

@RestControllerAdvice是什么?
@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成,而@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
@RestControllerAdvice的特点:

[*]通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。
[*]注解了@RestControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。
[*]@RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。
[*]@ExceptionHandler:用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。
[*]@InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
[*]@ModelAttribute:本来作用是绑定键值对到Model中,当与@ControllerAdvice配合使用时,可以让全局的@RequestMapping都能获得在此处设置的键值对
@ControllerAdvice可以指定Controller范围

[*]省略时,默认全局生效
[*]basePackages:指定一个或多个包,这些包及其子包下的所有Controller都被该 @ControllerAdvice 管理。
[*]basePackageClasses:是basePackages的一种变形,指定一个或多个Controller类,这些类所属的包及其子包下的所有Controller 都被该@ControllerAdvice管理。
[*]assignableTypes:指定一个或多个Controller类,这些类被该@ControllerAdvice管理。
[*]annotations:指定一个或多个注解,被这些注解所标记的Controller会被该@ControllerAdvice管理。
@RestControllerAdivce可以让我们处理异常时,返回结果按自己定义的类型进行返回。
一般用于全局异常的处理,如下
@RestControllerAdvice(basePackages = "cn.codewei.aopstudy.controller")
public class ControllerExceptionHandler {
    @ExceptionHandler(value = RuntimeException.class)
    public String runtimeExceptionHandler(RuntimeException e) {
      return "runtimeExceptionHandler: " + e.getMessage();
    }
}2. ResponseBodyAdvice

ResponseBodyAdvice接口是在 Controller执行return之后,在response返回给客户端之前,执行的对response的一些处理,可以实现对response数据的一些统一封装或者加密等操作。
该接口一共有两个方法:

[*]supports:判断是否要执行beforeBodyWrite方法,true为执行,false不执行。通过supports方法,我们可以选择哪些类或哪些方法要对response进行处理,其余的则不处理。
[*]beforeBodyWrite:对 response 处理的具体执行方法。
如:
@RestControllerAdvice(basePackages = "cn.codewei.aopstudy.controller")
public class RewriteHandler implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
      // 如果标记了RewriteResponse注解(自定义的注解),则通过(需要进行处理)
      boolean b = returnType.hasMethodAnnotation(RewriteResponse.class);
      if (b){
            return true;
      }
      return false;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
      String s = String.valueOf(body);
      return s + " - RewriteHandler";// 修改返回结果
    }
}3. RequestBodyAdvice

在实际项目中,往往需要对请求参数做一些统一的操作,例如参数的过滤,字符的编码,第三方的解密等等,Spring提供了RequestBodyAdvice一个全局的解决方案,免去了我们在Controller处理的繁琐。
RequestBodyAdvice仅对使用了@RequestBody注解的生效 , 因为它原理上还是AOP,所以GET方法是不会操作的。
@ControllerAdvice(basePackages = "cn.codewei.controller")//此处设置需要当前Advice执行的域 , 省略默认全局生效
public class GlobalRequestBodyAdvice implements RequestBodyAdvice {

    /** 此处如果返回false , 则不执行当前Advice的业务 */
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
//      return methodParameter.getMethod().isAnnotationPresent(XXApiReq.class);
      return false;
    }

    /**
   * @title 读取参数前执行
   * @description 在此做些编码 / 解密 / 封装参数为对象的操作
   *
   **/
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
      return new XHttpInputMessage(inputMessage, "UTF-8");
    }

    /**
   * @title 读取参数后执行
   * @author Xingbz
   */
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
      return inputMessage;
    }

    /**
   * @title 无请求体时的处理
   */
    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
      return body;
    }
}

//这里实现了HttpInputMessage 封装一个自己的HttpInputMessage
class XHttpInputMessage implements HttpInputMessage {
    private HttpHeaders headers;
    private InputStream body;

    public XHttpInputMessage(HttpInputMessage httpInputMessage, String encode) throws IOException {
      this.headers = httpInputMessage.getHeaders();
      this.body = encode(httpInputMessage.getBody(), encode);
    }

    private InputStream encode(InputStream body, String encode) {
      //省略对流进行编码的操作
      return body;
    }

    @Override
    public InputStream getBody() {
      return body;
    }

    @Override
    public HttpHeaders getHeaders() {
      return null;
    }
}Spring默认提供了接口的抽象实现类RequestBodyAdviceAdapter , 我们可以继承这个类按需实现 , 让代码更简洁一点
public abstract class RequestBodyAdviceAdapter implements RequestBodyAdvice {

        @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
        @Nullable
        public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage,
                        MethodParameter parameter, Type targetType,
                        Class<? extends HttpMessageConverter<?>> converterType) {

                return body;
        }
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: ResponseBodyAdvice与@RestControllerAdvice