SpringBoot全局异常处理与自定义异常处理

下层基础决定上层建筑,所以我今天研究了一波异常处理机制。

为什么要研究“下层基础”呢?

首先,当只有把基础设施给建设完全我们才能快速进行开发,因为SpringBoot相对于Spring而言,已经提高了我们的开发效率,我们只需要在此基础上再完善,最终才能帮助我们快速开发。

话不多说,接下来讲讲我研究的一种思路。

全局异常处理

我们首先在SpringBoot工程下创建一个名为exception的包,然后在包下面新建一个类叫GlobalException ,我们利用@ControllerAdvice注解来捕获异常。

public class GlobalException {
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map getSysError(Exception e) {
        Map<String,Object> map = new HashMap<>();
        map.put("code", -1);
        map.put("msg", e.getMessage());
        map.put("request",RequestHelper.getRequestUrl());
        return map;
    }
}    

getSysError 方法就是捕获系统出现的异常的一个方法,然后RequestHelper是我封装的一个获取请求方法和请求URL的类。具体实现如下:

public class RequestHelper {

    public static HttpServletRequest getRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request;
    }
    public static String getRequestUrl() {
        String methods = getRequest().getMethod();
        return methods+" "+getRequest().getServletPath();
    }
}

然后,根据全局异常处理看看捕获到的异常结果是怎么样的,我们利用 1/0这个大家都很熟悉的一个异常作为例子。

@RestController
public class HelloController {
    @GetMapping("/hello")
    public int hello() {
        return 1 / 0;
    }
}

返回结果如下:

{
"msg": "/ by zero",
"request": "GET /hello",
"code": -1
}

但是需要注意的一点是,在部署上线项目的时候,我们不能直接去返回具体的系统错误信息,因此,在生产环境中,我们需要修改为“msg”:"服务器开小差了",类似于这种模糊的错误信息。

自定义异常处理

新建一个HttpException的自定义异常基础类,

@Data
public class HttpException extends RuntimeException {
    private String msg;
    private int code;
    private String url = RequestHelper.getRequestUrl();
}

之后所有的自定义异常类都去继承HttpException,然后还有很重要的一点是为什么HttpException不去继承Exception这个类,而去继承RuntimeException这个类呢?原因请看
Java自定义异常,应该继承Exception还是Runtime Exception,为什么?

然后在此示例中,我创建了一个NotFound类。

@Data
public class NotFound extends HttpException {

    private String msg = "资源不存在";
    private int code = 999;

    public NotFound(String msg) {
        this.msg = msg;
    }
    public NotFound() {

    }
}

然后在GlobalExceptin 中添加以下代码:

@ControllerAdvice
public class GlobalException {
    // 系统异常捕获
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map getSysError(Exception e) {
        Map<String,Object> map = new HashMap<>();
        map.put("code", -1);
        map.put("msg", e.getMessage());
        map.put("request",RequestHelper.getRequestUrl());
        return map;
    }
	// 自定义异常捕获
    @ResponseBody
    @ExceptionHandler(value = HttpException.class)
    public Map errorHandler(HttpException ex) {
        Map<String,Object> map = new HashMap<>();
        map.put("code", ex.getCode());
        map.put("msg", ex.getMsg());
        map.put("request",ex.getRequest());
        return map;
    }
}

我们在@ExceptionHandler(value = HttpException.class)中还是捕获了 HttpException这个类,这就是为什么之前 我们所有的自定义异常类全部继承HttpException这个基类的原因之一了。

然后我们再来测试下NotFound这个异常。

@RestController
public class HelloController {
    @GetMapping("/hello")
    public int hello() {
        throw new NotFound();
    }
}    

结果如下:

{
"msg": "资源不存在",
"request": "GET /hello",
"code": 999
}

至此,异常处理就已经完成了。