全局异常处理

  • 这种方式可以更方便的对一个类的所有方法都进行异常处理,写好异常类后我们只需使用注解添加到类和方法上即可
  • 异常处理一般2种

    • 返回错误信息
    • 返回一个公共错误页面

      自定义异常类

  • 实现一个BaseException,其他业务异常继承该基类

    1. @Data
    2. @EqualsAndHashCode(callSuper = true)
    3. public class BaseException extends RuntimeException {
    4. private Integer code;
    5. private String message;
    6. //默认使用系统抛出的异常消息,也可以自定义消息
    7. public BaseException(Status status) {
    8. super(status.getMessage());
    9. this.code = status.getCode();
    10. this.message = status.getMessage();
    11. }
    12. public BaseException(Integer code, String message) {
    13. super(message);
    14. this.code = code;
    15. this.message = message;
    16. }
    17. }
    1. @Getter
    2. public class JsonException extends BaseException {
    3. public JsonException(Status status) {
    4. super(status);
    5. }
    6. public JsonException(Integer code, String message) {
    7. super(code, message);
    8. }
    9. }
  • 信息类 ```java @Getter public enum Status { /**

    • 操作成功 */ OK(200, “操作成功”),

      /**

    • 未知异常 / UNKNOWN_ERROR(500, “服务器出错啦”); /*
    • 状态码 / private Integer code; /*
    • 内容 */ private String message;

      Status(Integer code, String message) { this.code = code; this.message = message; } }

  1. - result返回信息封装类
  2. ```java
  3. package com.xkcoding.exception.handler.model;
  4. import com.xkcoding.exception.handler.constant.Status;
  5. import com.xkcoding.exception.handler.exception.BaseException;
  6. import lombok.Data;
  7. /**
  8. * <p>
  9. * 通用的 API 接口封装
  10. * </p>
  11. *
  12. * @author yangkai.shen
  13. * @date Created in 2018-10-02 20:57
  14. */
  15. @Data
  16. public class ApiResponse {
  17. /**
  18. * 状态码
  19. */
  20. private Integer code;
  21. /**
  22. * 返回内容
  23. */
  24. private String message;
  25. /**
  26. * 返回数据
  27. */
  28. private Object data;
  29. /**
  30. * 无参构造函数
  31. */
  32. private ApiResponse() {
  33. }
  34. /**
  35. * 全参构造函数
  36. *
  37. * @param code 状态码
  38. * @param message 返回内容
  39. * @param data 返回数据
  40. */
  41. private ApiResponse(Integer code, String message, Object data) {
  42. this.code = code;
  43. this.message = message;
  44. this.data = data;
  45. }
  46. /**
  47. * 构造一个自定义的API返回
  48. *
  49. * @param code 状态码
  50. * @param message 返回内容
  51. * @param data 返回数据
  52. * @return ApiResponse
  53. */
  54. public static ApiResponse of(Integer code, String message, Object data) {
  55. return new ApiResponse(code, message, data);
  56. }
  57. /**
  58. * 构造一个成功且带数据的API返回
  59. *
  60. * @param data 返回数据
  61. * @return ApiResponse
  62. */
  63. public static ApiResponse ofSuccess(Object data) {
  64. return ofStatus(Status.OK, data);
  65. }
  66. /**
  67. * 构造一个成功且自定义消息的API返回
  68. *
  69. * @param message 返回内容
  70. * @return ApiResponse
  71. */
  72. public static ApiResponse ofMessage(String message) {
  73. return of(Status.OK.getCode(), message, null);
  74. }
  75. /**
  76. * 构造一个有状态的API返回
  77. *
  78. * @param status 状态 {@link Status}
  79. * @return ApiResponse
  80. */
  81. public static ApiResponse ofStatus(Status status) {
  82. return ofStatus(status, null);
  83. }
  84. /**
  85. * 构造一个有状态且带数据的API返回
  86. *
  87. * @param status 状态 {@link Status}
  88. * @param data 返回数据
  89. * @return ApiResponse
  90. */
  91. public static ApiResponse ofStatus(Status status, Object data) {
  92. return of(status.getCode(), status.getMessage(), data);
  93. }
  94. /**
  95. * 构造一个异常且带数据的API返回
  96. *
  97. * @param t 异常
  98. * @param data 返回数据
  99. * @param <T> {@link BaseException} 的子类
  100. * @return ApiResponse
  101. */
  102. public static <T extends BaseException> ApiResponse ofException(T t, Object data) {
  103. return of(t.getCode(), t.getMessage(), data);
  104. }
  105. /**
  106. * 构造一个异常且带数据的API返回
  107. *
  108. * @param t 异常
  109. * @param <T> {@link BaseException} 的子类
  110. * @return ApiResponse
  111. */
  112. public static <T extends BaseException> ApiResponse ofException(T t) {
  113. return ofException(t, null);
  114. }
  115. }

定义异常转发器

  • 异常转发器即对不同异常类定义不同方法,这样无需控制器或者服务层创建设置异常信息对象,直接调异常转发器即可
  • @ControllerAdvice用于声明全局信息(即表示该类可以使用定义的全局规则),最常用于与@ExceptionHandler结合来实现全局异常处理
    • 该注解就是在@Controller基础上增加了声明全局的功能,如果需要全类使用字符串交互,使用**@RestControllerAdvice**
  • @ExceptionHandler标注于方法上,表示使用一个全局异常
    • @ExceptionHandler(value = JsonException.class)JsonException是自定义的一个异常类
    • 该方法单独使用时用于控制器类的非控制器方法上,表示控制器方法发生异常时就调用该方法
      @ControllerAdvice
      public class UserController {
      @ExceptionHandler(RuntimeException.class)   //如果发生运行时异常...
      //  --------------注意可以定义request参数以获取发生异常的请求----------
      //可以自由设置返回类型。如返回响应模型
      public ModelAndView handleUnknowException(Exception ex,HttpServletRequest request) {
         return new ModelAndView("500.html", Map.of("error", ex.getClass().getSimpleName(), "message", ex.getMessage()));
      }
      ...
      }
      

@ControllerAdvice
@Slf4j
public class DemoExceptionHandler {
    private static final String DEFAULT_ERROR_VIEW = "error";

    /**
     * 统一 json 异常处理
     *
     * @param exception JsonException
     * @return 统一返回 json 格式
     */
    @ExceptionHandler(value = JsonException.class)
    @ResponseBody
    public ApiResponse jsonErrorHandler(JsonException exception) {
        log.error("【JsonException】:{}", exception.getMessage());
        return ApiResponse.ofException(exception);
    }

    /**
     * 统一 页面 异常处理
     *
     * @param exception PageException
     * @return 统一跳转到异常页面
     */
    @ExceptionHandler(value = PageException.class)
    public ModelAndView pageErrorHandler(PageException exception) {
        log.error("【DemoPageException】:{}", exception.getMessage());
        ModelAndView view = new ModelAndView();
        view.addObject("message", exception.getMessage());
        view.setViewName(DEFAULT_ERROR_VIEW);
        return view;
    }
}

抛出自定义异常

  • 需要抛出异常的地方throw new 异常类;

    @GetMapping("/page")
      public ModelAndView pageException() {
          if(用户未登录){
          throw new PageException(Status.UNKNOWN_ERROR);
          } else {
              ....
          }
      }
    

    全局响应处理

  • 直接配置个如下的全局响应处理即可

    @ControllerAdvice
    public class GloabController implements ResponseBodyAdvice<Object> {
      //该方法可以精确设置哪些类型的
      @Override
      public boolean supports(final MethodParameter returnType, final Class<? extends HttpMessageConverter<?>> converterType) {
          Class clazz=returnType.getClass();
          return returnType.getGenericParameterType().equals(HttpResult.class);
      }
    
      @Override
      public Object beforeBodyWrite(final Object body, final MethodParameter returnType, final MediaType selectedContentType,
                                    final Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                    final ServerHttpRequest request, final ServerHttpResponse response) {
          //方便自定义HttpResult时
        if (body == null || body instanceof HttpResult) {
              return body;
          }
          final HttpResult result=new HttpResult();
          result.setCode(200);
          result.setMsg("");
          result.setData(body);
          if (true) {// 2
              ObjectMapper objectMapper = new ObjectMapper();
              try {
                  return objectMapper.writeValueAsString(result);
              } catch (JsonProcessingException e) {
                  throw new RuntimeException("将 Response 对象序列化为 json 字符串时发生异常", e);
              }
          }
          return result;
      }
    }