在專案的實際執行過程中,總會不可避免的丟擲這樣或那樣的異常,異常也是我們分析系統狀況的一種途徑。對於異常的處理,如果在每個可能出現異常的程式碼塊中都做單獨的處理,無疑會加大開發人員的工作量,同時異常資訊也不好統一,後期維護也困難。
針對這種情況,最好的辦法是將異常處理從業務邏輯中解耦出來,這樣既保證了業務邏輯的功能單一,又實現了異常資訊的統一維護。另外,如果不想將異常資訊直接拋給使用者還可以方便的對異常進行攔截做二次封裝,已返回給使用者比較友好的提示資訊。
這裡主要介紹在SpringBoot中如何使用
@ControllerAdvice
和
@ExceptionHandler
統一處理異常。
建立統一的返回物件
public class CommonExceptionResult { private long code; private String message; public CommonExceptionResult(long code, String message) { this。code = code; this。message = message; } getter和setter…… }
定義統一的JSON返回結構,主要包括:異常碼、異常資訊。當然,如果有需要還可以新增其他屬性,如請求的url、請求引數等。這裡主要做示例,只新增兩個基本屬性。
異常攔截處理
@ControllerAdvice@ResponseBodypublic class CommonExceptionHandler { private static final Logger logger = LoggerFactory。getLogger(CommonExceptionHandler。class); /** * 攔截空指標異常 * */ @ExceptionHandler(NullPointerException。class) public CommonExceptionResult handleNPLException(NullPointerException ex) { logger。error(“【空指標異常】:{}”, ex。getMessage()); return new CommonExceptionResult(1234, ex。getMessage()); } 其它型別異常的處理…… }
這裡主要用到了
@ControllerAdvice
和
@ExceptionHandler
,這兩個註解都是SpringBoot提供用來處理異常的。
@ControllerAdvice
註解主要用來開啟全域性的異常捕獲,
@ExceptionHandler
註解用來指定捕獲那種具體型別的異常,並進入到方法體中做處理。而@ResponseBody註解則給呼叫者返回一個JSON格式的資料。
被
@ControllerAdvice
註解修飾的類會被
Spring
容器管理,另外,該註解還有一個
basePackages
屬性,用來指定捕獲哪些包中的異常,如果不指定則捕獲所有包的異常。
如果在
@ExceptionHandler
中指定的是
Exception
類,則一個方法就能攔截所有的異常,不過這樣一來就不在有任何意義,我們無法透過返回資訊判斷具體的異常也就無法定位、分析問題。
測試
以空指標異常為例,看一下實際執行效果。
@RestControllerpublic class UserController { @Autowired private UserService userService; @GetMapping(“/printuserinfo”) public void pringUserInfo() { userService。printUserInfo(); }}
@Servicepublic class UserService { public void printUserInfo() { throw new NullPointerException(“com。chou。easyspringboot。easyspringbootexception。service。UserService。printUserInfo空指標了”); }}
使用postman模擬請求介面,可以看到如下的返回結果。
說明我們的攔截類起作用了。
自定義異常的處理
實際開發中,我們也會自定義一些專案中的業務異常,對於這類異常,使用上述方法同樣有效。
列舉自定義異常
public enum ExceptionCode { PARAMETER_EXCEPTION(0000, “引數異常”), MSG_CONVERT_EXCEPTION(0001, “訊息轉換異常”); 省略其它自定義異常…… private long code; private String message; private ExceptionCode(long code, String message) { this。code = code; this。message = message; } public long getCode() { return code; } public String getMessage() { return message; }}
我們可以使用一個列舉來維護所有的業務異常,同樣主要包含兩個屬性:異常碼和提示資訊,這樣再出現業務異常時透過異常碼便可得知異常型別。
自定義異常類
public class MsgConvertException extends RuntimeException { private long code; private String message; public MsgConvertException() { this。code = ExceptionCode。MSG_CONVERT_EXCEPTION。getCode(); this。message = ExceptionCode。MSG_CONVERT_EXCEPTION。getMessage(); } public MsgConvertException(String message) { this。code = ExceptionCode。MSG_CONVERT_EXCEPTION。getCode(); this。message = message; } public long getCode() { return code; } @Override public String getMessage() { return message; } /** * 允許設定自定義提示資訊,不允許設定自定義異常碼 * */ public void setMessage(String message) { this。message = message; }}
@ControllerAdvice和@ExceptionHandler攔截自定義異常
@ControllerAdvice@ResponseBodypublic class CommonExceptionHandler { private static final Logger logger = LoggerFactory。getLogger(CommonExceptionHandler。class); /** * 攔截自定義的訊息轉換異常 * */ @ExceptionHandler(MsgConvertException。class) public CommonExceptionResult handlingMsgConvertException(MsgConvertException ex) { logger。error(“【引數異常】:{}”, ex。getMessage()); return new CommonExceptionResult(ex。getCode(), ex。getMessage()); } 攔截其它異常…… }
處理結果
微信公眾號:程式設計師Mark Chou