@RestControllerAdvice是什么

@RestControllerAdvice是Spring框架提供的一个注解,用于定义全局异常处理器和全局数据绑定设置。它结合了@ControllerAdvice和@ResponseBody两个注解的功能。

@ControllerAdvice

@ControllerAdvice是一个用于定义全局控制器增强(即全局异常处理和全局数据绑定)的注解。通过使用@ControllerAdvice,我们可以将异常处理和数据绑定逻辑集中到一个类中,避免在每个控制器中重复编写相同的异常处理代码。

@ResponseBody

@ResponseBody是用于指示控制器方法返回的对象将被直接写入响应体中的注解。它告诉Spring将方法的返回值序列化为JSON或其他适当的响应格式,并将其作为HTTP响应的主体返回给客户端。

@RestControllerAdvice作用

当我们在类上使用@RestControllerAdvice注解时,它相当于同时使用了@ControllerAdvice和@ResponseBody。这意味着被@RestControllerAdvice注解标记的类将被视为全局异常处理器,并且异常处理方法的返回值将以JSON格式直接写入响应体中。

通过在@RestControllerAdvice类中定义异常处理方法,我们可以捕获和处理控制器中抛出的异常,提供自定义的异常处理逻辑,以及返回适当的响应给客户端。这样可以统一处理应用程序中的异常情况,提高代码的可维护性和可读性。

常见可被捕获的异常类型

下面是常见可以被@RestControllerAdvice捕获的异常类型

  1. Exception:普通的Java异常,是大多数其他异常的基类。
  2. RuntimeException:运行时异常,包括NullPointerException、IllegalArgumentException等。
  3. HTTP状态码异常:例如,HttpStatus.NOT_FOUND表示资源未找到,HttpStatus.BAD_REQUEST表示请求错误等。
  4. ValidationException:数据验证异常,例如使用JSR-303或Hibernate Validator进行的数据验证失败。
  5. MethodArgumentNotValidException:请求方法参数验证失败时抛出的异常。
  6. HttpMessageNotReadableException:当请求的HTTP消息无法读取或解析时抛出的异常,例如请求体格式错误。
  7. HttpRequestMethodNotSupportedException:当请求的HTTP方法不受支持时抛出的异常。
  8. MissingServletRequestParameterException:当请求缺少必需的参数时抛出的异常。
  9. BindException:数据绑定失败时抛出的异常,通常与表单提交相关。
  10. ConstraintViolationException:当使用Bean验证(如Hibernate Validator)时,违反约束条件时抛出的异常。
  11. NoHandlerFoundException:当找不到适合处理当前请求的处理程序时抛出的异常。
  12. 自定义的异常类

项目中实战

项目依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

整体项目结构

img

全局返回实体类R

全局返回实体类

import lombok.Data;

import java.io.Serializable;

@Data
public class R implements Serializable {

    private Integer code;
    private String message;
    private Object data;
    private static final Integer DEFAULT_SUCCESS_CODE = 200;
    private static final Integer DEFAULT_ERROR_CODE = 500;

    /**
     * 成功
     *
     * @return
     */
    public static R success() {
        R r = new R();
        r.setCode(DEFAULT_SUCCESS_CODE);
        r.setMessage("请求成功");
        r.setData(new Object());
        return r;
    }

    /**
     * 成功-data
     *
     * @return
     */
    public static R success(Object data) {
        R r = new R();
        r.setCode(DEFAULT_SUCCESS_CODE);
        r.setMessage("请求成功");
        r.setData(data);
        return r;
    }

    /**
     * 成功-data和message
     *
     * @return
     */
    public static R success(Object data, String message) {
        R r = new R();
        r.setCode(DEFAULT_SUCCESS_CODE);
        r.setMessage(message);
        r.setData(data);
        return r;
    }

    /**
     * 错误
     *
     * @return
     */
    public static R error() {
        R r = new R();
        r.setCode(DEFAULT_ERROR_CODE);
        r.setMessage("请求失败");
        return r;
    }

    /**
     * 错误-code
     *
     * @return
     */
    public static R error(Integer code) {
        R r = new R();
        r.setCode(code);
        r.setMessage("请求失败");
        return r;
    }

    /**
     * 错误-message
     *
     * @return
     */
    public static R error(String message) {
        R r = new R();
        r.setCode(DEFAULT_ERROR_CODE);
        r.setMessage(message);
        return r;
    }

    /**
     * 错误-code和message
     *
     * @return
     */
    public static R error(Integer code, String message) {
        R r = new R();
        r.setCode(code);
        r.setMessage(message);
        r.setData(new Object());
        return r;
    }
}

自定义异常CustomException类

有些场景需要抛出自定义异常,就可以写一个继承RuntimeException类的异常

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author csn
 * @date 2024/2/27
 */

@EqualsAndHashCode(callSuper = true)
@Data
public class CustomException extends RuntimeException{

    public CustomException(String message) {
        super(message);
    }
}

GlobalExceptionHandler类

捕获对应的异常类型,并给出相应的提示

import com.example.test.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * RestControllerAdvice = ControllerAdvice + ResponseBody
 * 是controller的增强器,可以全局捕获spring mvc抛的异常
 * 优雅异常处理程序
 * 统一拦截处理
 * 在该类中,可以定义多个方法,不同的方法处理不同的异常
 */
@RestControllerAdvice(annotations = RestController.class)
@Slf4j
public class GlobalExceptionHandler {


    /**
     * 处理Validated验证异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({BindException.class})
    public R bindExceptionHandler(BindException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        log.error("BindException:", e);
        return R.error(objectError.getDefaultMessage());
    }

    /**
     * 处理单个文件超大异常
     *
     * @param e
     * @return
     * @ExceptionHandler 注解用来指明异常的处理类型
     */
    @ExceptionHandler(FileSizeLimitExceededException.class)
    public R fileSizeLimitExceededExceptionHandler(FileSizeLimitExceededException e) {
        log.error("FileSizeLimitExceededException异常:", e);
        return R.error("单个文件大小不允许超过2MB");
    }

    /**
     * 处理请求数据超大异常
     *
     * @param e
     * @return
     * @ExceptionHandler
     */
    @ExceptionHandler(SizeLimitExceededException.class)
    public R sizeLimitExceededExceptionHandler(SizeLimitExceededException e) {
        log.error("SizeLimitExceededException异常:", e);
        return R.error("请求数据大小不允许超过10MB");
    }

    /**
     * 处理NullPointerException异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({NullPointerException.class})
    public R customExceptionHandler(NullPointerException e) {
        log.error("NullPointerException异常:", e);
        return R.error("空指针异常");
    }

    /**
     * 处理CustomExceptionHandler异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler({CustomException.class})
    public R customExceptionHandler(CustomException e) {
        log.error("CustomException异常:", e);
        return R.error(e.getMessage());
    }

    /**
     * 处理其它异常:统一捕获,写入异常日志
     * 例1:HttpRequestMethodNotSupportedException 路由请求方式异常
     * 例2:NullPointerException 空指针异常
     * 例3:BadSqlGrammarException sql异常
     * ......
     *
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public R exceptionHandler(Exception e) {
        log.error("Exception异常:", e);
        return R.error();
    }
}

TestController类

在TestController类中写三个接口

第一个接口:抛出1/0异常

第二个接口:抛出自定义异常

第二个接口:抛出空指针异常

import com.example.test.exception.CustomException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author csn
 * @date 2024/3/18
 */
@RestController
public class TestController {

    @GetMapping("/test1")
    public String testError1() {
        int i = 1/0;
        return "test1";
    }

    @GetMapping("/test2")
    public String testError2() {
        String a = "aa";
        if (!a.equals("a")){
            throw new CustomException("自定义异常");
        }
        return "test2";
    }

    @GetMapping("/test3")
    public String testError3() {
        String a = null;
        if (a.equals("a")){
        }
        return "test3";
    }

}

调用接口报错提示

  1. test1接口:

img

  1. test2接口:

img

  1. test3接口

img

注意事项

  1. @RestControllerAdvice并不能捕获所有异常,例如Error类的子类(如OutOfMemoryError)通常无法被捕获。
  2. 某些异常可能会被其他全局异常处理器或框架层面的异常处理机制捕获,而不会被@RestControllerAdvice处理。
  3. Error及其子类:Error是Throwable的子类,表示严重的错误,通常由虚拟机抛出,如OutOfMemoryError、StackOverflowError等。这些异常通常意味着应用程序处于不可恢复的状态,因此无法被@RestControllerAdvice捕获。
  4. ThreadDeath:ThreadDeath是Error的子类,它表示线程意外终止的异常。与其他Error一样,ThreadDeath异常也无法被@RestControllerAdvice捕获。
  5. VirtualMachineError及其子类:VirtualMachineError是Error的子类,表示与Java虚拟机相关的错误,如InternalError、UnknownError等。这些错误通常与虚拟机的内部状态或配置有关,无法被@RestControllerAdvice捕获。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐