天天看點

業務中 自定義異常用 Exception 還是 RuntimeException? 為什麼?

作者:網際網路架構小馬哥

今天和同僚 聊了下異常 相關的事,整理在此

目前公司中使用的 自定義異常是 extend RuntimeException

繼承異常

我們在業務開發中 繼承異常是extend RuntimeException 還是 Exception呢

一想 這肯定是 RuntimeException 啊,但是這是為什麼呢? 選擇 Exception 不行麼?

RuntimeException和Exception差別

聊這個之前 我們需要确定我們的需求是什麼

需求

需求是 自定義一個 業務中使用的異常

确定了需求 接下來我們就要分析

業務中 自定義異常用 Exception 還是 RuntimeException? 為什麼?

到這裡 知道 我們業務開發 基本都是運作時異常 是以用 RuntimeException

到這裡有一個疑問,我們平時 IO操作的時候 動不動就爆紅 讓我們throws 或者 try catch,是哪個屬性做到的?

首先看一段代碼

業務中 自定義異常用 Exception 還是 RuntimeException? 為什麼?

但是這個異常是因為createNewFile 方法 主動抛出的異常 和 是不是 RuntimeException 沒關系啊

public boolean createNewFile() throws IOException {
    SecurityManager security = System.getSecurityManager();
    if (security != null) security.checkWrite(path);
    if (isInvalid()) {
        throw new IOException("Invalid file path");
    }
    return fs.createFileExclusively(path);
}
複制代碼           

是有關系的,非RuntimeException 都是需要throws 或者try catch 但是這個不是自動的,是開發人員寫代碼的時候 需要做的,代表這個異常是 必須被檢查的

而其他需要注意的 引用 程式設計語言原理 書中 第434頁的一部分

業務中 自定義異常用 Exception 還是 RuntimeException? 為什麼?

這塊對于業務中使用哪個 先留一個疑問 往下看

業務中 自定義異常用 Exception 還是 RuntimeException? 為什麼?

怎麼才算其他RuntimeException 代碼上有什麼差別?

沒有差別就是一個繼承 RuntimeException,另一個繼承 Exception,别的沒有了

事務中攔截異常

我們到這 知道了繼承異常應該用RuntimeException,但是我們應該知道 阿裡規約手冊 中有這麼一段

1、讓檢查異常也復原:你就需要在整個方法前加上@Transactional(rollbackFor=Exception.class)

2、讓非檢查異常不復原:

需要加入@Transactional(notRollbackFor=RunTimeException.class)

3、不需要事務管理(or 日志丢失)

需要加入@Transactional(propagation=Propagation.NOT_SUPPORTED)

為什麼?

Exception類下面除了runtimeException還有SQLException和ioException

如果方法沒有抛出runtimeException 而是抛出 SQLException和ioException那麼事務是不會復原的

那麼這就結束了嗎?在我們編碼過程中,如果方法要抛出一些可檢查異常時是需要throws進行顯式指定異常類的

那麼問題來了,我們都知道方法簽名中預設是throws RuntimeException,已知SqlException不是RuntimeException的子類

小總結

@Transactional(notRollbackFor=RunTimeException.class) 是因為抛棄了 IO異常和 SQL異常等情況,是以 我們 應該用 Transactional(rollbackFor=Exception.class)

為什麼 不是 rollbackFor = Throwable.class 呢

不需要顯式指定 rollbackFor = Throwable.class ,因為如果發生錯誤,spring将預設復原事務。**

原本就是除了 非RuntimeException 别的已經在事務管理中了

在其預設配置中,Spring Framework的事務基礎結構代碼僅标記事務對于運作時復原,未經檢查的異常;也就是說,抛出的異常是RuntimeException的執行個體或子類。 (錯誤也會 - 預設情況下會導緻復原) 。從事務方法抛出的已檢查異常不會導緻在預設配置中復原。**

或者檢視 DefaultTransactionAttribute

public boolean rollbackOn(Throwable ex) { 
        return (ex instanceof RuntimeException || ex instanceof Error); 
}
複制代碼           

具體位置 docs.spring.io/spring-fram…

總結下來事務方面應該使用 @Transactional(rollbackFor=Exception.class)

其他中間件 對異常的應用場景 和 為什麼?

先看nacos

//檢查 異常
public class NacosException extends Exception {
}
複制代碼           

在使用的時候

業務中 自定義異常用 Exception 還是 RuntimeException? 為什麼?

有throws 的 也有 try catch的

那麼 是什麼抛出Nacos 異常的呢?

private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass)
        throws NacosException {
    try {
        xxx
    } catch (Exception e) {
        throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);
    }
    throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");
}
複制代碼           

rocketmq

先看一個 運作時異常

運作時異常
public class AclException extends RuntimeException {
}
複制代碼           

使用

public static void verify(String netaddress, int index) {
    if (!AclUtils.isScope(netaddress, index)) {
        //運作的時候 發生異常
        throw new AclException(String.format("Netaddress examine scope Exception netaddress is %s", netaddress));
    }
}
複制代碼           

再看一個 檢查異常

public class MQClientException extends Exception {
}
複制代碼           
throws MQClientException, RemotingException, MQBrokerException
複制代碼           

使用

public TransactionSendResult sendMessageInTransaction(final Message msg,
    final LocalTransactionExecuter localTransactionExecuter, final Object arg)
    throws MQClientException {
    TransactionListener transactionListener = getCheckListener();
    if (null == localTransactionExecuter && null == transactionListener) {
        //如果為空 client 異常
        throw new MQClientException("tranExecutor is null", null);
    }
 }
複制代碼           

總結

中間件中大部分都是 client 連接配接失敗,遠端連接配接逾時,server端異常 這種,屬于檢查時異常,是以應該 extend Exception

但是

  1. 業務開發的時候 大部分都是 判斷空,屬于運作時異常 推薦 RunntimeException
  2. 檢查時異常 需要一直 throws,從代碼整潔度上 推薦 RunntimeException

總結

  1. 遠端連接配接的使用 / 異常 使用檢查時異常,讓另一端能感受到異常
  2. 業務中的代碼使用 運作時異常

我還是推薦業務使用 extends RuntimeException,其餘的就需要根據業務場景選擇了

思考

ok 那你說 遠端連接配接使用 檢查時異常,那feign 屬于遠端rpc,他的異常就必須是 檢查時異常麼?為什麼?

連結:https://juejin.cn/post/7166976169152086024

繼續閱讀