程序抛出的异常,业务逻辑抛出的异常,都可以使用全局异常捕获来统一处理,可以减少很多冗余代码。
入门
新建一个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