【SpringBoot】面向生产的统一异常处理方式

1.Spring MVC异常统一处理的三种方式

  • 使用 @ ExceptionHandler 注解:对当前所在Controller的异常进行处理
  • 实现 HandlerExceptionResolver 接口
  • 使用@controlleradvice+@ ExceptionHandler注解:对全局异常进行处理(推荐)

@Controller + @ExceptionHandler

缺点:进行异常处理的方法必须与出错的方法在同一个Controller里面,不能全局控制异常。每个类都要写一遍:

@Controller
public class GlobalController {
    
    

   /**
     * 用于处理异常的
     * @return
     */
    @ExceptionHandler({
    
    MyException.class})
    public String exception(MyException e) {
    
    
        System.out.println(e.getMessage());
        e.printStackTrace();
        return "exception";
    }

    @RequestMapping("test")
    public void test() {
    
    
        throw new MyException("出错了!");
    }
}

实现 HandlerExceptionResolver 接口

可以进行全局的异常控制

@Component
@Slf4j
public class GlobalDefaultExceptionHandler implements HandlerExceptionResolver {
    
    
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
                                         Exception ex) {
    
    
        log.info("==============Exception Start 000000=============");
        if (ex instanceof BaseException) {
    
    
            log.debug(ex, ex);
        }
        else {
    
    
            log.error(ex, ex);
        }
        log.info("==============Exception End 000000=============");
        //AJAX请求
        if (NetworkUtil.isAjax(request)) {
    
    
            String msg = null;
            String code = null;
            String detail = null;
            if (ex instanceof BaseException) {
    
    
                msg = ((BaseException) ex).getErrorMsg();
                code = ((BaseException) ex).getErrorCode();
                detail = ((BaseException) ex).getMsgDetail();
            }
            else {
    
    
                FSTErrorCode fc = FSTErrorCode.SYS_ERROR_000000;
                msg = fc.getErrorMsg();
                code = fc.getErrorCode();
                detail = fc.getMsgDetail();
            }
            try {
    
    
                JSONObject result = new JSONObject();
                result.put("msg", msg);
                result.put("code", code);
                result.put("detail", detail);
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().print(result.toString());
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
            return null;
        }
        //非Ajax请求
        else {
    
    
            ModelAndView mv = new ModelAndView();
            mv.setViewName("error/error");//跳转到resources/template/error/error.html
            mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>"));
            return mv;
        }
    }
}

@ControllerAdvice+@ExceptionHandler

  • 可以实现全局的异常捕获
@ControllerAdvice
@ResponseBody
@Slf4j
public class WebExceptionHandle {
    
    
    private static Logger logger = LoggerFactory.getLogger(WebExceptionHandle.class);
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ServiceResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
    
    
        log.error("参数解析失败", e);
        return ServiceResponseHandle.failed("could_not_read_json");
    }

    /**
     * 405 - Method Not Allowed
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ServiceResponse handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
    
    
        log.error("不支持当前请求方法", e);
        return ServiceResponseHandle.failed("request_method_not_supported");
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ServiceResponse handleHttpMediaTypeNotSupportedException(Exception e) {
    
    
        log.error("不支持当前媒体类型", e);
        return ServiceResponseHandle.failed("content_type_not_supported");
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public ServiceResponse handleException(Exception e) {
    
    
        if (e instanceof BusinessException){
    
    
            return ServiceResponseHandle.failed("BUSINESS_ERROR", e.getMessage());
        }

        log.error("服务运行异常", e);
        e.printStackTrace();
        return ServiceResponseHandle.failed("server_error");
    } 
}

2.统一响应返回

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiResult<T> implements Serializable {
    
    
    private static final long serialVersionUID = 411731814484355577L;
    private int code;
    private String msg;
    private boolean isSuccess;
    private T data;

    public static ApiResult<String> success() {
    
    
        return success("success");
    }

    public static <T> ApiResult<T> success(T data) {
    
    
        return (ApiResult<T>) ApiResult.builder().code(0).msg("操作成功").isSuccess(true).data(data).build();
    }

    public static ApiResult<String> fail() {
    
    
        return fail(-1);
    }

    public static ApiResult<String> fail(int code) {
    
    
        return fail(code, "fail");
    }
    public static ApiResult<String> fail(int code,String message) {
    
    
        return fail(code, message);
    }


    public static <T> ApiResult<T> fail(T data) {
    
    
        return fail(-1, data);
    }

    public static <T> ApiResult<T> fail(int code, T data) {
    
    
        return (ApiResult<T>) ApiResult.builder().code(code).msg("操作失败").isSuccess(false).data(data).build();
    }

    public static <T> ApiResult<T> success(int code, String message, T data) {
    
    
        return (ApiResult<T>) ApiResult.builder().code(code).msg(message).isSuccess(true).data(data).build();
    }

    public static <T> ApiResult<T> fail(int code, String message, T data) {
    
    
        return (ApiResult<T>) ApiResult.builder().code(code).msg(message).isSuccess(false).data(data).build();
    }

    @Override
    public String toString() {
    
    
        return "ApiResult(responseCode=" + this.getCode() + ", responseMsg=" + this.getMsg() + ", isSuccess=" + this.isSuccess() + ", data=" + this.getData() + ")";
    }
}

3.统一异常处理

  1. 使用@ControllerAdvice+ @ExceptionHandler注解进行异常全局捕获;
  2. 定义一个通用的异常捕获方法,便于捕获未定义的异常信息;
  3. 自定一个异常类,捕获针对项目或业务的异常;

自定义异常

@Data
public class BusinessException extends RuntimeException {
    
    
    private Integer code;

    public BusinessException(Integer code, String message) {
    
    
        super(message);
        this.code = code;
    }

    public BusinessException(ResultCodeEnum resultCodeEnum) {
    
    
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }

    @Override
    public String toString() {
    
    
        return "BusinessException{" + "code=" + code + ", message=" + this.getMessage() + '}';
    }
}

异常类型统一使用枚举类管理

@Getter
public enum ResultCodeEnum {
    
    
    SUCCESS(true, 20000, "成功"),
    UNKNOWN_ERROR(false, 20001, "未知错误"),,
    PARAM_ERROR(false, 20002, "参数错误"),
    ;

    // 响应是否成功
    private Boolean success;
    // 响应状态码
    private Integer code;
    // 响应信息
    private String message;

    ResultCodeEnum(boolean success, Integer code, String message) {
    
    
        this.success = success;
        this.code = code;
        this.message = message;
    }
}

定义全局异常捕获器

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    

    /**-------- 通用异常处理方法 --------**/
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ApiResult error(Exception e) {
    
    
        e.printStackTrace();
        return ApiResult.fail(-1,"通用异常");
    }

    /**-------- 指定异常处理方法 --------**/
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public ApiResult error(NullPointerException e) {
    
    
        e.printStackTrace();
        log.error(ExceptionUtil.getMessage(e));
        return ApiResult.fail(-1,"空指针异常");
    }

    @ExceptionHandler(HttpClientErrorException.class)
    @ResponseBody
    public ApiResult error(IndexOutOfBoundsException e) {
    
    
        e.printStackTrace();
        log.error(ExceptionUtil.getMessage(e));
        return ApiResult.fail(-1,"下标越界异常");
    }

    /**-------- 自定义定异常处理方法 --------**/
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ApiResult error(BusinessException e) {
    
    
        e.printStackTrace();
        log.error(ExceptionUtil.getMessage(e));
        return ApiResult.fail(-1,"业务异常");
    }
}

【转载】Java统一异常处理及架构实战
https://juejin.cn/post/6844904033488994317

https://github.com/purgeteam/unified-dispose-springboot

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/103238687