异常处理

异常处理


异常处理问题分析

1、异常如何处理

问题引入

  • 针对代码中的异常,常规有两种处理方式,一种throws直接抛出,另一种try…catch捕获。
  • 在项目开发中,有可能存在人为逻辑的异常,也可能为取得异常的详情,或是保证程序在异常时继续向下执行,会采用第二种处理方式。
  • 问题:代码中每一处异常都来捕获,会使代码冗余且不利于维护

解决思路

  • 定义一个全局异常处理类,返回统一规范的异常信息;
  • 处理逻辑,先判定是否会出现异常,再执行后续具体的业务。

业务举例

  • 增加员工,处理流程:1先根据员工编号查询员工对象,2判断员工对象是否有信息,即是否不为空,3若有信息,则说明已存在,无需再添加,若不是,则直接添加
public class MyService {
    // 注入dao层
    @Autowired
    EmployeeecMapper employeeecMapper;
    /**
     * 添加员工信息
     * @param employee 员工对象
     * @return 影响的行数
     */
    public int add(Employee employee) {
        // 根据id查询员工对象
        Employeeec emp = employeeecMapper.selectByPrimaryKey(employee.getId());
        // 判断是否已有该员工
        if (emp != null){
            // 已有,抛出异常,异常信息为已有该员工
            throw new RuntimeException("异常代码:1201,错误信息:该员工已存在");
        }
        // 没有,插入该员工
        return employeeecMapper.insert(emp);
    }
}
2、异常处理流程
  • 业务中存在运行时异常和业务逻辑异常,前者不运行时很难察觉,后者在遍及业务时就可以定义出来,因此异常分为不可预知异常和可知异常。处理流程如下:
    1. 自定义全局异常类,使用@ControllerAdvice,控制器增强
    2. 自定义错误代码及错误信息,两种异常最终会采用统一的信息格式来表示,错误代码+错误信息。
    3. 对于可预知的异常由程序员在代码中主动抛出,由SpringMVC统一捕获。定义异常信息类,提供错误代码和错误信息,捕获自定义异常时,直接将该对象返回。
    4. 不可预知异常通常是由于系统出现bug、或一些外界因素(如网络波动、服务器宕机等),异常类型为RuntimeException类型(运行时异常)。定义一个map,将常见的异常存入其中,并定义错误代码。对于其他不常见的异常,即map中没有的,同一一个异常对象返回即可。
3、异常处理代码流程
  • 可知异常

    1、定义打印异常信息与返回结果的接口

public interface IErrorCode {
    // 操作结果代码
    int getCode();
    // 提示信息
    String getMessage();
}

2、定义打印异常信息的枚举类和返回结果类

@ToString
public enum ResultCode implements IErrorCode {
    SUCCESS(200, "操作成功"),
    VALIDATE_FAILED(400, "Invalid input(无效的输入)"),
    UNAUTHORIZED(401, "暂未登录或token已经过期"),
    FORBIDDEN(403, "403 Forbidden(请求被拒绝)没有相关权限"),
    NOTFOUND(404, "not found(没有找到相关资源)"),
    VALIDATE_METHOD(405, "请求方法被禁止"),
    FAILED(500, "服务器内部错误"),
    METHOD_ARGUMENT_NOT_VALID(10000,"参数校验失败"),
    REPETITIVE_OPERATION(10001, "请勿重复操作"),
    ACCESS_LIMIT(10002, "请求太频繁, 请稍后再试"),
    EXEC_FAILED(10003, "操作失败");
    private int code;
    private String message;
    private ResultCode(int code, String message) {
        this.code = code;
        this.message = message;
    }
    @Override
    public int getCode() {
        return code;
    }
    @Override
    public String getMessage() {
        return message;
    }
}
/**
 * 通用返回对象
 */
@ApiModel(description = "结果对象")
@Data
@ToString
public class CommonResult<T> {
    @ApiModelProperty(value = "状态码",required = true)
    private int code;
    @ApiModelProperty(value = "消息",required = true)
    private String message;
    @ApiModelProperty(value = "数据",required = true)
    private T data;
    protected CommonResult() {
    }
    protected CommonResult(int code, String message) {
        this.code = code;
        this.message = message;
    }
    protected CommonResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    /**
     * 成功返回结果
     *
     */
    public static  CommonResult success() {
        return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage());
    }
    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     */
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }
    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     * @param  message 提示信息
     */
    public static <T> CommonResult<T> success(T data, String message) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
    }
    /**
     * 失败返回结果
     * @param errorCode 错误码
     */
    public static <T> CommonResult<T> failed(IErrorCode errorCode) {
        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage());
    }
    /**
     * 失败返回结果
     * @param code 错误码
     * @param message 错误信息
     */
    public static <T> CommonResult<T> failed(int code,String message) {
        return new CommonResult<T>(code, message);
    }
    /**
     * 失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<T>(ResultCode.FAILED.getCode(), message);
    }
    /**
     * 失败返回结果
     */
    public static <T> CommonResult<T> failed() {
        return failed(ResultCode.FAILED);
    }
    /**
     * 参数验证失败返回结果
     */
    public static <T> CommonResult<T> validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }
    /**
     * 参数验证失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> validateFailed(String message) {
        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message);
    }
    /**
     * 参数校验失败
     * @param data   详细信息
     * @param <T>
     * @return
     */
    public static <T> CommonResult<T> methodArgumentNotValid(T  data){
        return new CommonResult<T>(ResultCode.METHOD_ARGUMENT_NOT_VALID.getCode(),ResultCode.METHOD_ARGUMENT_NOT_VALID.getMessage(),data);
    }
    /**
     * 未登录返回结果
     */
    public static <T> CommonResult<T> unauthorized() {
        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage());
    }
    /**
     * 未授权返回结果
     */
    public static <T> CommonResult<T> forbidden(T data) {
        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage());
    }
    /**
     * 请求方法被禁止返回结果
     */
    public static <T> CommonResult<T> validateMethodFailed() {
        return new CommonResult<T>(ResultCode.VALIDATE_METHOD.getCode(), ResultCode.VALIDATE_METHOD.getMessage());
    }
}

3、定义错误异常类

/**
 * @author Admin
 * API异常类
 */
@Data
public class CustomException extends RuntimeException {
    private int code;
    private String msg;
    public CustomException(){}
    public CustomException(IErrorCode resultCode){
        this.code = resultCode.getCode();
        this.msg = resultCode.getMessage();
    }
    public CustomException(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

4、定义异常抛出类

/**
 * @author Admin
 * 异常抛出类
 */
public class ExceptionCast {
    /**
     * 抛出异常
     * @param resultCode
     */
    public static void cast(ResultCode resultCode){
        throw new CustomException(resultCode);
    }
    /**
     * 抛出异常
     * @param code 状态码
     * @param msg 异常消息
     */
    public static void cast(int code,String msg){
        throw new CustomException(code,msg);
    }
}

5、定义异常捕获类,使用ControllerAdviceRestControllerAdvice控制器增强的注解,并在捕获APIException异常的方法上加ExceptionHandler注解,即可捕获该类的所有异常,返回json数据。

/**
 * @author Admin
 * 全局异常处理增强
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionAdvice {
    /**
     * 捕获CustomException类异常
     * @param e
     * @return 结果信息,json数据
     */
    @ExceptionHandler(CustomException.class)
    public CommonResult customExceptionHandler(ApiException e) {
        log.error("CustomException: ", e);
        return CommonResult.failed(e.getCode(),e.getMsg());
    }
 }

6、在业务中抛出异常

public class MyService {
    @Autowired
    private EmployeeecMapper employeeecMapper;
    public int add(Employee employee) {
        Employeeec emp = employeeecMapper.selectByPrimaryKey(employee.getId());
        if (emp != null){
            ExceptionCast.cast(ResultCode.EXEC_FAILED);
        }
        return employeeecMapper.insert(emp);
    }
}
  • 不可知异常处理

1、类似可知异常,先在ResultCode类中添加错误代码,如:

FORBIDDEN(403, "403 Forbidden(请求被拒绝)没有相关权限"),

2、在异常捕获类中添加不可知异常的捕获方法。该方法中,定义一个只读的map存储异常类型的错误代码的映射,map中没有的元素,统一用错误代码9999来定义。

UNKNOWN_ERROR(9999,"未知异常")
// 定义map,存储常见错误信息。该类map不可修改
private static ImmutableMap<Class<? extends Throwable>,ResultCode> EXCEPTIONS;
// 构建ImmutableMap
protected static ImmutableMap.Builder<Class<? extends Throwable>,ResultCode> builder = ImmutableMap.builder();
static {
        builder.put(HttpMessageNotReadableException.class,ResultCode.VALIDATE_FAILED);
        builder.put(HttpRequestMethodNotSupportedException.class,ResultCode.VALIDATE_METHOD);
        builder.put(BindException.class,ResultCode.VALIDATE_FAILED);
}
@ExceptionHandler(Exception.class)
public CommonResult exceptionHandler(Exception e) {
    log.error("Exception: ", e);
    if(exceptions == null){
        exceptions = builder.build();
    }
    ResultCode resultCode = exceptions.get(e.getClass());
    if (resultCode != null) {
        return CommonResult.failed(resultCode);
    }else{
        return CommonResult.failed(ResultCode.UNKNOWN_ERROR);
    }
}
 /**
 * 捕获CustomException类异常
 * @param e
 * @return 结果信息,json数据
 */
@ExceptionHandler(CustomException.class)
public CommonResult customExceptionHandler(CustomException e) {
    log.error("CustomException: ", e);
    return CommonResult.failed(e.getCode(),e.getMsg());
}
/**
 * 捕获数据校验异常
 * @param e
 * @return
 */
@ExceptionHandler(MethodArgumentNotValidException.class)
public CommonResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
    log.error("MethodArgumentNotValidException: ", e);
    ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
    return CommonResult.methodArgumentNotValid(objectError.getDefaultMessage());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值