Spring Boot 異常處理

img

前言

先談談「異常處理」這件事。下面有 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

總結:使用異常而不是錯誤碼,優雅地使用異常!函數應該只作一件事,處理錯誤就是一件事。所以,處理錯誤的函數不應作其餘事!框架

在 Spring Boot 中處理異常

一、默認的異常處理

例如 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) 能處理異常嗎?答:能夠的!因此對於局部比較公用的異常能夠定義一個父類,拋出異常時能夠拋出具體的子類異常,處理時,處理父類異常便可(即只用寫一個方法處理一系列相似的異常)

© 著做權歸做者全部,轉載或內容合做請聯繫做者

img

拒絕黑盒應用-Spring Boot 應用可視化監控

併發Bug之源有三,請睜大眼睛看清它們

史上最輕鬆入門之Spring Batch - 輕量級批處理框架實踐

Spring Cloud Gateway - 快速開始

APM工具尋找了一圈,發現SkyWalking纔是個人真愛

Spring Boot 注入外部配置到應用內部的靜態變量

將 HTML 轉化爲 PDF新姿式

Java 使用 UnixSocket 調用 Docker API

Fastjson致命缺陷

Service Mesh - gRPC 本地聯調遠程服務

使用 Thymeleaf 動態渲染 HTML

原文連接:

本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索