spring统一异常处理

对于与数据库相关的 Spring MVC 项目,我们通常会把 事务 配置在 Service层,当数据库操作失败时让 Service 层抛出运行时异常,Spring 事物管理器就会进行回滚。

如此一来,我们的 Controller 层就不得不进行 try-catch Service 层的异常,否则会返回一些不友好的错误信息到客户端。

但是,Controller 层每个方法体都写一些模板化的 try-catch 的代码,很难看也难维护,特别是还需要对 Service 层的不同异常进行不同处理的时候。

/**
 * 手动处理 Service 层异常和数据校验异常的示例
 * @param dog
 * @param errors
 * @return
 */
@PostMapping(value = "")
AppResponse add(@Validated(Add.class) @RequestBody Dog dog, Errors errors){
    AppResponse resp = new AppResponse();
    try {
        // 数据校验
        BSUtil.controllerValidate(errors);

        // 执行业务
        Dog newDog = dogService.save(dog);

        // 返回数据
        resp.setData(newDog);

    }catch (BusinessException e){
        LOGGER.error(e.getMessage(), e);
        resp.setFail(e.getMessage());
    }catch (Exception e){
        LOGGER.error(e.getMessage(), e);
        resp.setFail("操作失败!");
    }
    return resp;
}

本文讲解使用 @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理,只要设计得当,就再也不用在 Controller 层进行 try-catch 了!

而且,@Validated 校验器注解的异常,也可以一起处理,无需手动判断绑定校验结果 BindingResult/Errors 了!

 

1.@ControllerAdvice 注解定义全局异常处理类

//统一在这个类中处理异常,全局拦截指定的异常
@ControllerAdvice
public class ExceptionHandle {

 

2.@ExceptionHandler 注解声明异常处理方法

//统一在这个类中处理异常,全局拦截指定的异常
@ControllerAdvice
public class ExceptionHandle {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

    @ExceptionHandler(value = Exception.class)  //申明捕获那个异常类
    @ResponseBody  //返回给浏览器的是一个json格式,上面又没有@RestController,所以在此申明@ResponseBody
    public Result handle(Exception e) {
       return “错误!”;
    }
}

方法 handleException() 就会处理所有 Controller 层抛出的 Exception 及其子类的异常。

 

实施统一异常管理的案例:

1.将返回结果信息进行封装,新建Result类

public class Result<T> {

    /**
     * 错误码
     */
    private Integer code;

    /**
     * 提示信息
     */
    private String msg;

    /**
     * 具体内容
     */
    private T data;

    public Result() {
    }

    public Result(Integer code, String msg) {
        this(code, msg, null);
    }

    public Result(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

   //。。。
}

 

2.为方便对结果信息进行处理,新建结果信息工具类:ResultUtils

public class ResultUtil {

    public static Result success(Object object) {
        return new Result(0, "成功", object);
    }

    public static Result success() {
        return success(null);
    }

    public static Result error(Integer code, String msg) {
        return new Result(code, msg);
    }


}

 

3.为了方便对异常信息进行处理,自定义异常信息类:GirlException

public class GirlException extends RuntimeException { //这个地方不要写exception,因为Spring是只对运行时异常进行事务回滚,
                                                        //如果抛出的是exception是不会进行事务回滚的。
    private Integer code;

    public GirlException(ResultEnum resultEnum) {
        this(resultEnum.getMsg(), resultEnum.getCode());
    }

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

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

 

4.为方便统一管理异常代码和信息之间的关系,建立枚举类

public enum ResultEnum {
    
    UNKOWN_ERROR(-1, "未知错误"),
    SUCCESS(0, "成功"),
    PRIMARY_SCHOOL(10, "你可能还在上小学"),
    MIDDLE_SCHOOL(16, "你可能还在上初中"),
    ;
    
    private Integer code;
    
    private String  msg;
    
    private ResultEnum(Integer code,String msg) {
        this.msg = msg;
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public Integer getCode() {
        return code;
    }
        
}

 

5.在service层抛出异常

public void getAge(Integer id) throws Exception {
        Girl girl = girlRepository.getOne(id);
        Integer age = girl.getAge();
        if (age < 10) {
            throw new GirlException(ResultEnum.PRIMARY_SCHOOL);
        } else if (age > 10 && age < 16) {
            throw new GirlException(ResultEnum.MIDDLE_SCHOOL);
        }

    }

 

6.在controller层继续抛异常

 //统一异常处理,在controller层中的exception传到exceptionHandler去处理
    @GetMapping(value = "girls/getAge/{id}")
    public void getAge(@PathVariable("id") Integer id) throws Exception {
        girlService.getAge(id);
    }

 

7.在exceptionHandler中处理异常

//统一在这个类中处理异常,全局拦截指定的异常
@ControllerAdvice
public class ExceptionHandle {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

    @ExceptionHandler(value = Exception.class)  //申明捕获那个异常类
    @ResponseBody  //返回给浏览器的是一个json格式,上面又没有@RestController,所以在此申明@ResponseBody
    public Result handle(Exception e) {
        if (e instanceof GirlException) {
            GirlException girlException = (GirlException) e;
            return ResultUtil.error(girlException.getCode(), girlException.getMessage());
        }
        logger.error("【系统异常】", e);
        return ResultUtil.error(ResultEnum.UNKOWN_ERROR.getCode(), ResultEnum.UNKOWN_ERROR.getMsg());
    }
}

 

posted @ 2018-03-04 16:49  开拖拉机的蜡笔小新  阅读(5641)  评论(3编辑  收藏  举报