java web 異常實踐

異常實踐

前言

本文是我在項目中設(shè)計和處理異常的一些實踐,主要是圍繞著常見的web項目,歡迎大家指正。

本文分為兩個個部分

  1. 異常設(shè)計
  2. 異常處理

異常設(shè)計

通常考慮異常設(shè)計時大致分為三個部分

  1. 接口層
  2. 業(yè)務(wù)層
  3. 類庫

接口層就是我們通常說的 controller 層,以及提供 rpc 服務(wù)的接口層。

業(yè)務(wù)層就是主要的業(yè)務(wù)代碼模塊,主要是 service 層。

類庫主要是指一些公共模塊,可以在各個項目中使用的,比如 json,分布式鎖等。

業(yè)務(wù)層

在業(yè)務(wù)層,我們主要是要設(shè)計業(yè)務(wù)異常。什么是業(yè)務(wù)異常?業(yè)務(wù)異常就是我們能夠人為的判斷出業(yè)務(wù)邏輯走到某一個位置是不對的。比如,我們要根據(jù)一個 uid 來修改一個 user 的 name,但我們發(fā)現(xiàn)并沒有這個 uid 對應(yīng)的 user 數(shù)據(jù),這時候就應(yīng)該拋出一個業(yè)務(wù)異常。在發(fā)生業(yè)務(wù)異常時,要避免拋出 npe,RuntimeException 等其他內(nèi)置異常,以方便上層來分辨到底是業(yè)務(wù)錯誤還是程序 bug。

我一般會設(shè)計一個業(yè)務(wù)異常的基類 ServiceException,將所有業(yè)務(wù)異常以這種類型來拋出,并帶有必要的 message。

為了把用戶可讀的消息和開發(fā)人員可讀的消息區(qū)別開,ServiceException 還需要實現(xiàn)一個接口 UserMessage,并實現(xiàn)其中方法 getUserMessage()來返回用戶可讀的信息,而 getMessage() 可以攜帶更詳細(xì)的開發(fā)人員可讀的錯誤信息

設(shè)計一個 ServiceErrorException,繼承 ServiceExceptionServiceErrorException 的主要目的是為了表明這個異常的錯誤程度高,需要記錄 error。

以上就定型了業(yè)務(wù)異常的基本結(jié)構(gòu),上面一些特殊設(shè)計會在異常處理中用到,我們后面來說,再做前后對照。我們可以根據(jù)需要來實現(xiàn)若干子類來表示業(yè)務(wù)層中不同模塊的錯誤。

接口層

對于接口層,特別是rpc調(diào)用,比如我們的dubbo調(diào)用,需要把api的jar包放在調(diào)用方。我們需要把異常類給包括進(jìn)去,但調(diào)用方不能也不應(yīng)該拿到我們業(yè)務(wù)層的 ServiceException,所以需要在接口層定義新的業(yè)務(wù)異常類型,比如,就叫ApiServiceException,放在api的jar包里給調(diào)用方。

接口實現(xiàn)需要把業(yè)務(wù)層的ServiceException給catch到,重新封裝為ApiServiceException拋出。

這樣,調(diào)用方在判斷調(diào)用時發(fā)生的異常時,有三種可能:

  1. rpc框架異常。比如又dubbo框架拋出的異常,這一版兩種可能:1. 網(wǎng)絡(luò)異常,我們需要重試;2. 調(diào)用未能達(dá)成,這種一般是接口沒有匹配上,在開發(fā)測試時都可以發(fā)現(xiàn)的錯誤,改掉即可。所以,當(dāng)發(fā)生rpc框架異常時,調(diào)用方的策略就應(yīng)該是重試。
  2. ApiServiceException。這表示被調(diào)用方出現(xiàn)了業(yè)務(wù)異常,調(diào)用方也需要作為業(yè)務(wù)異常來處理。
  3. 其他異常。這表示被調(diào)用方的程序有bug報出了異常透傳給了調(diào)用方,這是調(diào)用方應(yīng)及時聯(lián)系接口實現(xiàn)方來修補bug。

以上,就能夠分類準(zhǔn)確應(yīng)對rpc過程中的異常情況。

http方式的接口層也可以這么做,不過由于api并不對外,所以也可以完全由自身來處理異常類型,詳見異常處理部分。

類庫

作為類庫,因為通常沒有業(yè)務(wù)意義,所以在發(fā)生邏輯上的異常時,根本不可能知道需要怎么處理,這就需要直接向上拋出,到交給業(yè)務(wù)層處理。

類庫需要將自身的邏輯上的異常,同一封裝。比如,處理 Json 的類庫,異常最終拋出時,都被封裝成為JsonParseExceptionJsonSerializeExcption

這樣調(diào)用方使用類庫時,異常會有兩種:

  1. 類庫封裝的自定義異常。這種是調(diào)用時出現(xiàn)的邏輯錯誤,調(diào)用方以業(yè)務(wù)異常來處理。
  2. 其他異常。可以認(rèn)為是類庫bug。

異常處理

有了以上的異常設(shè)計,那么處理時就可以按照以下流程。

以 http 請求的 ExceptionHandler 為例,所有 http 請求異常都會放在這里處理,過程:

  1. ex 異常傳入。
  2. 裝飾 ex 異常,ex = new WebApiException(...,ex),其包含有 messageisLogError 屬性
    1. 如果 ex 是非 ServiceException,那么message = “系統(tǒng)內(nèi)部錯誤”isLogError = true
    2. 如果 ex 是 ServiceException,那么message = ex.getUserMessage();更進(jìn)一步,如果是 ServiceErrorException,那么isLogError = true,否則 isLogError = false
  3. 如果 ex.isLogError == true,記錄 error log,否則,記錄 warn log。
  4. 判斷 http 請求是頁面請求,還是ajax請求。
    1. 如果是頁面請求。500轉(zhuǎn)錯誤頁,顯示 ex.getMessage(),如果是debug環(huán)境或者是請求帶有debug參數(shù),也把錯誤堆棧輸出在頁面上。
    2. 如果是 ajax 請求。返回表示錯誤的 json 消息,同樣,如果是debug環(huán)境或者是請求帶有debug參數(shù),消息中帶上堆棧信息。

以上,就是一個簡單而有效的異常處理機制。

最后

以上是我的個人實踐經(jīng)驗總結(jié),請各位批評指正,歡迎討論。

歡迎加入群 661035226,gradle,spring,activiti 交流

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,287評論 25 708
  • 來源與:阿里云棲 禁止用于商業(yè)用途 ps:如果需要電子書 評論你們郵箱 我會發(fā)給你們 下面感覺還是有點亂 目錄 一...
    小向資源網(wǎng)閱讀 7,668評論 0 12
  • 這是頹廢的一周,也是回家的一周 做的事情 1.回家 2.《算法數(shù)據(jù)結(jié)構(gòu)javascript》完成75%,當(dāng)然感覺最...
    醬油_閱讀 180評論 0 0
  • 啟動頁不顯示,LaunchImage不起作用如果項目中原來使用了LaunchScreen.xib,現(xiàn)在改為xcss...
    囧書閱讀 661評論 0 0