SpringBoot中的全局异常处理

2018-06-18 00:06:20来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

在实际项目中,如果出现了异常,我们不希望直接把异常抛给用户,应该对异常进行处理,然后返回一个友好的信息给用户。这节主要总结一下项目中如何使用SpringBoot如何拦截全局的异常。

1. 定义返回的json结构

请求接口需要返回json数据,一般后台会统一定义一个返回给前端的数据结构,包括code、msg信息等,这可以参考【1】SpringBoot返回Json数据及封装中封装的统一json结构,如下:

public class JsonResult {
    /**
     * 异常码
     */
    protected String code;

    /**
     * 异常信息
     */
    protected String msg;

    protected JsonResult() {}
    
    public JsonResult(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    // get set
}

 

2. 处理系统异常

新建一个GlobalExceptionHandler全局异常处理类,然后加上@ControllerAdvice注解即可拦截项目中抛出的异常,该注解中也可以跟上basePackages属性,用来指定拦截哪个包中的异常,一般我们可以不指定,所有异常都拦截。

@ControllerAdvice
public class GlobalExceptionHandler {
    
}

 

下面举几个例子来说明一下如何使用。

2.1 处理不合法的请求格式异常

有些时候,请求格式不合法,会抛出TypeMismatchException,我们可以拦截该异常,做一个友好处理:

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * 不合法的请求格式异常
     * @param ex
     * @return
     */
    @ExceptionHandler(TypeMismatchException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public JsonResult handleTypeMismatchException(TypeMismatchException ex) {
        logger.error("不合法的请求格式", ex);
        return new JsonResult("400", "不合法的请求格式");
    }
}

 

2.2 HTTP参数不可读异常

有些时候,请求参数不合法,会抛出HttpMessageNotReadableException,我们可以拦截该异常,做一个友好处理:

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * HTTP参数不可读
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public JsonResult handleHttpMessageNotReadableException(
            HttpMessageNotReadableException ex) {
        logger.error("请求参数不可读", ex);
        return new JsonResult("400", "请求参数不可读");
    }
}

 

2.3 一劳永逸?

当然了,异常很多,比如还有RuntimeException,数据库还有一些查询或者操作异常等等。由于Exception异常是父类,所有异常都会继承该异常,所以我们可以直接拦截Exception异常:

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * 系统异常 预期以外异常
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public JsonResult handleUnexpectedServer(Exception ex) {
        logger.error("系统异常:", ex);
        return new JsonResult("500", "系统发生异常,请联系管理员");
    }
}

 

但是项目中,我们一般都会比较详细的去拦截一些常见异常,拦截Exception虽然可以一劳永逸,但是不利于我们去排查或者定位问题。实际项目中,可以把拦截Exception异常写在GlobalExceptionHandler最下面,如果都没有找到,最后再拦截一下Exception异常,保证输出信息友好。

3. 拦截自定义异常

在实际项目中,除了拦截一些系统异常外,在某些业务上,我们需要自定义一些异常,比如在微服务中,服务之间的相互调用很平凡,很常见。要处理一个服务的调用,那么可能会调用失败,此时我们需要自定义一个异常,当调用失败时抛出来,给GlobalExceptionHandler去捕获。

3.1 定义异常信息

由于在业务中,有很多异常,针对不同的业务,可能给出的提示信息不同,所以为了方便项目管理,我们一般会定义一个异常信息枚举类。

/**
 * 业务异常提示信息枚举类
 * @author shengwu ni
 */
public enum BusinessMsgEnum {
    /** 参数异常 */
    PARMETER_EXCEPTION("102", "参数异常!"),
    /** 等待超时 */
    SERVICE_TIME_OUT("103", "服务调用超时!"),
    /** 参数过大 */
    PARMETER_BIG_EXCEPTION("102", "输入的图片数量不能超过50张!"),
    /** 500 : 一劳永逸的提示也可以在这定义 */
    UNEXPECTED_EXCEPTION("500", "系统发生异常,请联系管理员!");
    // 还可以定义更多的业务异常

    /**
     * 消息码
     */
    private String code;
    /**
     * 消息内容
     */
    private String msg;

    private BusinessMsgEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    // set get方法
}

 

3.2 拦截自定义异常

然后我们定义一个业务异常,当出现业务异常时,我们就抛这个自定义的业务异常即可。

/**
 * 自定义业务异常
 * @author shengwu ni
 */
public class BusinessErrorException extends RuntimeException {
    
    private static final long serialVersionUID = -7480022450501760611L;

    /**
     * 异常码
     */
    private String code;
    /**
     * 异常提示信息
     */
    private String message;

    public BusinessErrorException(BusinessMsgEnum businessMsgEnum) {
        this.code = businessMsgEnum.code();
        this.message = businessMsgEnum.msg();
    }
    // get set方法
}

 

构造方法中,传入我们上面自定义的异常枚举类,所以在项目中,如果有信息异常信息,我们直接在枚举类中添加即可,然后再拦截该异常时获取即可。

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     * 拦截业务异常,返回业务异常信息
     * @param ex
     * @return
     */
    @ExceptionHandler(BusinessErrorException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonResult handleBusinessError(BusinessErrorException ex) {
        String code = ex.getCode();
        String message = ex.getMessage();
        return new JsonResult(code, message);
    }
}

 

在业务代码中,我们可以直接在抛出业务异常,测试一下:

@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/exception")
    public String testException() {
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            throw new BusinessErrorException(BusinessMsgEnum.UNEXPECTED_EXCEPTION);
        }
        return null;
    }
}

 

运行一下项目,测试一下,返回json如下;

{"code":"500","msg":"系统发生异常,请联系管理员!"}

 

SpringBoot的全局异常拦截处理就总结这么多,在项目中运用也很广泛,基本上每个项目中都需要全局异常处理。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Java学习笔记二十九:一个Java面向对象的小练习

下一篇:转载:JSON技术的调研报告(四种常见的JSON格式对比及分析)