Spring Boot全局异常捕获

390 阅读2分钟

gitee链接

程序抛出的异常,业务逻辑抛出的异常,都可以使用全局异常捕获来统一处理,可以减少很多冗余代码。

入门

新建一个Spring Boot应用,模拟出一些异常:

package com.cc.controller;
​
import com.cc.exception.CustomException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
​
import java.util.Date;
​
@RestController
public class HelloController {
    // 正常请求
    @GetMapping("/t1")
    public String t1() {
        return "请求成功";
    }
​
    // 模拟算数运算异常
    @GetMapping("/t2")
    public String t2() {
        int a = 1/0;
        return "请求成功";
    }
​
    // 模拟空指针异常
    @GetMapping("/t3")
    public String t3() {
        Date date = null;
        date.before(new Date());
        return "请求成功";
    }
​
    // 抛出自定义异常
    @GetMapping("/t4")
    public String t4() {
        throw new CustomException("自定义异常");
    }
}

然后创建全局异常捕获类:

package com.cc.exception;
​
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
​
/**
 * 全局异常捕获
 * @author cc
 * @date 2021/06/16 15:10
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
​
    // 未注册的异常全部在这里处理
    @ExceptionHandler(value = Exception.class)
    public String catchException(Exception e) {
        e.printStackTrace();
        return e.getMessage();
    }
​
    /**
     * 空指针异常
     * @author cc
     * @date 2021-11-15 17:11
     */
    @ExceptionHandler(value = NullPointerException.class)
    public String catchNullPointerException(NullPointerException e) {
        return "空指针异常";
    }
​
    /**
     * 自定义异常
     * @author cc
     * @date 2021-11-15 17:12
     */
    @ExceptionHandler(value = CustomException.class)
    public String catchMethodArgumentNotValidException(CustomException e) {
        return e.getMessage();
    }
}

启动程序分别调用接口,可以看到异常都进入了GlobalExceptionHandler类,其中空指针异常和自定义异常被注册到GlobalExceptionHandler中,所以会进入各自的函数里面,而算术运算异常ArithmeticException没有被注册,则统一进入到Exception异常处理函数中。

进阶

我们现在可以统一处理异常了,不仅如此,我们还可以统一进行日志处理,除了在日志中输出错误信息以外,我们还能输出出现异常的类、函数甚至是所在行数,做到这点,我们进行日志排查问题的时候就能更加方便。

那么我们把全局异常捕获类改成:

package com.cc.exception;
​
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
​
/**
 * 全局异常捕获
 * @author cc
 * @date 2021/06/16 15:10
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
​
    // 未注册的异常全部在这里处理
    @ExceptionHandler(value = Exception.class)
    public String catchException(Exception e) {
        e.printStackTrace();
        todoErrorLogger(e);
        return e.getMessage();
    }
​
    /**
     * 空指针异常
     * @author cc
     * @date 2021-11-15 17:11
     */
    @ExceptionHandler(value = NullPointerException.class)
    public String catchNullPointerException(NullPointerException e) {
        todoErrorLogger(e);
        return "空指针异常";
    }
​
    /**
     * 自定义异常
     * @author cc
     * @date 2021-11-15 17:12
     */
    @ExceptionHandler(value = CustomException.class)
    public String catchMethodArgumentNotValidException(CustomException e) {
        todoErrorLogger(e);
        return e.getMessage();
    }
​
    private void todoErrorLogger(Exception e) {
        System.out.println("异常信息:" + e.getMessage());
        StackTraceElement[] elements = e.getStackTrace();
        if(elements.length > 0){
            StackTraceElement element = elements[0];
            System.out.println("异常所在类:" + element.getClassName());
            System.out.println("异常所在函数:" + element.getMethodName());
            System.out.println("异常所在行数: " + element.getLineNumber());
        }
        e.printStackTrace();
    }
}

调用接口模拟异常后,控制台会输出异常的更具体信息:

异常信息:/ by zero
异常所在类:com.cc.controller.HelloController
异常所在函数:t2
异常所在行数: 20