先談談「異常處理」這件事。下面有 2 份僞代碼,對比下:html
// ① 基於 if/else 判斷
if(deletePage(page) == E_OK){
if(registry.deleteReference(page.name) == E_OK){
if(configKeys.deleteKey(page.name.makeKey()) == E_OK){
logger.log("page deleted");
}else{
logger.log("configKey not deleted");
}
}else{
logger.log("deleteReference from registry failed");
}
}else{
logger.log("delete failed");
return E_RROR;
}複製代碼
// ② 基於異常處理
try{
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}catch(Exception e){
logError(e);
}複製代碼
能夠看出,若是使用異常替代返回錯誤碼,錯誤處理代碼就能從主路徑邏輯中分離出來,獲得簡化!json
②中,基於異常處理的代碼真的好嗎?實際上是醜陋不堪的,它搞亂了代碼結構,把錯誤處理與正常流程混爲一談。最好把 try 和 catch 代碼塊的主體部分抽離出來,造成另外的函數。併發
// ③ 優雅的異常處理邏輯
public void delete(Page page){
try{
deletePageAndAllReferences(page);
}catch(Exception e){
logError(e);
}
}
private void deletePageAndAllReferences(Page page) throw Exception{
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e){
logger.log(e.getMessage());
}複製代碼
③中,函數各司其職,更易於理解和修改了。app
總結:使用異常而不是錯誤碼,優雅地使用異常!函數應該只作一件事,處理錯誤就是一件事。所以,處理錯誤的函數不應作其餘事!框架
例如 401,404,500,5XX 等異常,Spring Boot 默認會跳轉到預配置的頁面,此處以 thymeleaf 模板引擎爲例:函數
+ resources
+ templates
+ error
- 401.html
- 404.html
- 500.html複製代碼
只需在 resources/templates/error/
路徑下添加對應的html文件便可。工具
局部異常通常處理業務邏輯出現的異常狀況,在 Controller 下使用 @ExceptionHandler
註解來處理異常。舉個小例子:this
先定義 ResponseBean 和 ExceptionEnum 兩個對象,輔助完成優雅的代碼。spa
/**
* 統一響應
* @author anoy
*/
public class ResponseBean<T> {
private int code;
private String message;
private T data;
public ResponseBean(){}
public ResponseBean(ExceptionEnum exceptionEnum){
this.code = exceptionEnum.getCode();
this.message = exceptionEnum.getMessage();
}
// 省略 setter/getter
}
複製代碼
/**
* 異常類型枚舉
* @author anoy
*/
public enum ExceptionEnum {
GIRL_FRIEND_NOT_FOUND(100000, "girl friend not found");
private int code;
private String message;
ExceptionEnum(int code, String message){
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}複製代碼
今天七夕,寫個 GirlFriendNotFoundException
(頗有同感,是否是?)3d
@Controller
public class UserController {
@RequestMapping("/friend/{id}")
public String friend(@PathVariable("id") Long id) throws GirlFriendNotFoundException {
if (id == 1L){
throw new GirlFriendNotFoundException();
}
return "friend";
}
@ExceptionHandler(GirlFriendNotFoundException.class)
@ResponseBody
public ResponseBean handleGirlFriendNotFound(GirlFriendNotFoundException exception){
loggerError(exception);
return new ResponseBean(ExceptionEnum.GIRL_FRIEND_NOT_FOUND);
}
private void logError(Exception e){
logger.error(e.getMessage());
}
}複製代碼
我的觀點:全局異常應該處理系統故障級別的問題,像參數校驗這種類型的異常,應該做爲局部異常來處理,例如 Redis 鏈接斷開,沒法請求數據,這種異常就應該當作全局異常來處理,在異常處理的邏輯中,還應該添加通知到開發人員的功能,方便開發人員及時處理錯誤!
全局異常處理,使用 @ControllerAdvice
和 @ExceptionHandler
來配合。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RedisConnectionFailureException.class)
public void handlerRedisConnectionFailureException(RedisConnectionFailureException exception){
logError(exception);
noticeToDev();
}
private void logError(Exception e){
logger.error(e.getMessage());
}
private void noticeToDev(){
// 通知具體開發人員
}
}複製代碼
一、局部異常和全局異常處理同一種類型的 Exception,會發生什麼結果?答:只會執行局部異常的處理邏輯!
二、GirlFriendNotFoundException 繼承了 RuntimeException, 使用@ExceptionHandler(RuntimeException.class) 能處理異常嗎?答:能夠的!因此對於局部比較公用的異常能夠定義一個父類,拋出異常時能夠拋出具體的子類異常,處理時,處理父類異常便可(即只用寫一個方法處理一系列相似的異常)
© 著做權歸做者全部,轉載或內容合做請聯繫做者
● 史上最輕鬆入門之Spring Batch - 輕量級批處理框架實踐
● APM工具尋找了一圈,發現SkyWalking纔是個人真愛
● Spring Boot 注入外部配置到應用內部的靜態變量
● Java 使用 UnixSocket 調用 Docker API
● Service Mesh - gRPC 本地聯調遠程服務
原文連接:
本文由博客一文多發平臺 OpenWrite 發佈!