前言

當我們用Spring Boot構建工程的時候,在處理各種複雜的業務需求時往往會涉及到各種各樣的異常,比如在進行文件的IO操作時會遇到IOExceptionFileNotFoundException,在編冩SQL語句或使用JDBC時會遇到SQLException,在編冩涉及反射相關的代碼時會遇到ClassCastException。除此之外還有許多常見的異常如空指針異常NullPointerException,數組下標越界異常ArrayIndexOutOfBoundsException,在使用迭代器遍曆集合時修改元素産生的異常ConcurrentModificationException,算數異常(如除0)ArithmeticException等等。

在處理這些異常時無非就是兩種選擇,最直接最省事的選擇是直接使用throws關鍵字拋出異常,讓上層的方法處理異常。另外一種方法就是用try-catch代碼塊捕捉異常。這兩種方法的缺點都很明顯,當工程量變大需要處理異常的地方逐漸變多時,如果一個一個的處理異常會顯得非常低效,而且也不方便進行統一管理。

那麼是否存在一種可以全局管理異常的方法呢。

RestControllerAdviceExceptionHandler

RestControllerAdvice標記的類可以用於處理全局異常。同時將ExceptionHandler標記在方法上可以處理對應的異常。

1
2
3
4
5
6
7
8
9
10
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

@ExceptionHandler(RuntimeException.class)
public Response exceptionHandler(RuntimeException e) {
log.error("Internal Server Error " + e);
return Response.error(e.getMessage(), ExceptionEnum.INTERNAL_SERVER_ERROR.getCode());
}
}

ExceptionHandler接受Class[]類型的參數,代表能處理的異常的類型。

定義基本的異常接口和枚舉類

1
2
3
4
5
public interface BaseException {

String getCode();
String getMsg();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum ExceptionEnum implements BaseException{
SUCCESS("200", "Success"),
BAD_REQUEST("400", "Bad Request"),
NOT_FOUND("404", "Not Found"),
METHOD_NOT_ALLOWED("405", "Method Not Allowed"),
INTERNAL_SERVER_ERROR("500", "Internal Server Error");

private final String code;
private final String msg;

ExceptionEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String getCode() {
return this.code;
}

@Override
public String getMsg() {
return this.msg;
}
}

定義一個Response類用於統一返回數據的格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Response {

private String msg;

private String code;

public static Response success(ExceptionEnum exceptionEnum) {
Response response = new Response(exceptionEnum.getMsg(), exceptionEnum.getCode());
return response;
}

public static Response error(ExceptionEnum exceptionEnum) {
Response response = new Response(exceptionEnum.getMsg(), exceptionEnum.getCode());
return response;
}

public static Response error(String msg, String code) {
Response response = new Response(msg, code);
return response;
}

public static Response error(String msg) {
Response response = new Response(msg, "-1");
return response;
}
}

測試一下,在controller中故意製造一個算數異常

1
2
3
4
5
@PutMapping("/add")
public String addPerson(@RequestBody Person person) {
int i = 1 / 0;
return myService.addPerson(person);
}

在postman中髮送請求

可以看到返回的msg是”/ by zero”正對應着算數異常,同時code是先前設置好的500。

這樣一來我們就能通過設置全局異常處理的方式來統籌管理整個項目中的所有異常,無需再一個一個關注具體的異常處理,可以將注意力全身心放在業務邏輯上,非常方便。