日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何处理java异常

發布時間:2023/12/14 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何处理java异常 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在開發業務系統中,我們目前絕大多數采用MVC模式,但是往往有人把service跟controller緊緊的耦合在一起,甚至直接使用Threadlocal來隱式傳值,并且復雜的邏輯幾乎只能使用service中存儲的全局對象來傳遞處理結果,包括異常。

這樣一來首先有違MVC模式,二來邏輯十分不清晰,難以維護.本文結合工作經驗,給出一些異常使用建議,使用spring來實戰異常為我們帶來的好處。

常常,我們讀罷了各種java的書,異常的各種機制,特性都很清楚,但是始終還是不知道如何使用,甚至背下了概念,卻不知道如何致用。

我們開發的業務系統,或者是產品,常常面臨著這樣的問題:

  • 系統運行出錯,但是完全不知道錯誤發生的位置。

  • 我們找到了錯誤的位置,但是完全不知道是因為什么。

  • 系統明明出了錯誤,但是就是看不到錯誤堆棧信息。

什么情況需要自定義異常

經??吹揭恍╉椖?#xff0c;在全局定義一個 AppException,然后所有地方都只拋出這個異常,并且把捕獲的異常case到這個AppException中。會有如下問題:

?

  • 浪費log日志存儲空間,并且棧頂并不是最接近發生異常的代碼位置。

  • 只有一種異常類,無法精準區分開異常類型。

  • 異常類后期難以修改以增加其攜帶的信息。

什么情況需要手動處理異常

我不會把書上的東西直接復制下來,這里說一下容易記住的,并且適合業務開發的。

  • 你有能力處理異常,并且你知道如何處理

  • 你有責任處理異常

自定義業務異常

考慮如下場景:系統提供一個API,用于修改用戶信息,服務器端采用json數據交互.首先我們定義ServiceException,用來表示業務邏輯受理失敗,它僅表示我們處理業務的時候發現無法繼續執行下去。

接下來看下Controller層.

?

關于上述Controller寫法乍一看會有一些冗余,如果無法理解,請仔細研讀MVC設計模式. 先不管service,我們來考慮下。 一個業務系統不可能不對用戶提交的數據進行驗證,驗證包括兩方面:有效性和合法性

  • 有效性: 比如用戶所在崗位,是否屬于數據庫有記錄的崗位ID,如果不存在,無效.

  • 合法性: 比如用戶名只允許輸入最多12個字符,用戶提交了20個字符,不合法.

有效性檢查,可以交給java的校驗框架執行,比如JSR303。假設用戶提交的數據經過驗證都合法,還是有一些情況是不能調用修改邏輯的。

  • 要修改的用戶ID不存在.

  • 用戶被鎖定,不允許修改.

  • 樂觀鎖機制發現用戶已經被被人修改過.

  • 由于某種原因,我們的程序無法保存到數據庫.

  • 一些程序員錯誤的開發了代碼,導致保存過程中出現異常,比如NPE.

  • 對于前3種,我們認為是有效性檢查失敗,第4種屬與我們無法處理的異常,第5種就是程序員bug。

    現在的問題是,前三種情況我們如何通知用戶呢?

  • 在ccontroller 調用userService的checkUserExist()方法.

  • 在controller直接書寫業務邏輯.

  • 在service響應一個狀態碼機制,比如1 2 3表示錯誤信息,0 表示沒有任何錯誤.

  • 顯然前2種方法都不可取,因為MVC不設計模式告訴我們,controller是用來接收頁面參數,并且調用邏輯處理,最后組織頁面響應的地方。我們不可以在controller進行邏輯處理,controller只應該負責用戶API入口和響應的處理(如若不然,思考一下如果有一天service的代碼打包成jar放到另一個平臺,沒有controller了,該怎么辦?)

    狀態碼機制是個不錯的選擇,可是如此一來,用戶保存邏輯變了,比如增加一個情況,不允許修改已經離職的用戶,那么我們還需要修改controller的代碼,代碼量增加,維護成本增高,并且還耦合了service,不符合MVC設計模式。

    那么怎么辦呢?現在我們來看下service代碼如何編寫

    這樣一來只要我們檢查到不允許保存的項目,我們就可以直接throw 一個新的異常,異常機制會幫助我們中斷代碼執行。

    接下來有2種選擇:

  • 在controller 使用try-catch進行處理.

  • 直接把異常拋給上層框架統一處理.

  • 第1種方式是不可取的,注意我們拋出的ServiceException,它僅僅邏輯處理異常,并且我們的方法前面沒有聲明throws ServiceException,這表示他是一個非受查異常.controller也沒有關心會發生什么異常。

    為什么不定義成受查異常呢?如果是一個受查異常,那么意味著controller必須要處理你的異常。并且如果有一天你的業務邏輯變了,可能多一種檢查項,就需要增加一個異常,反之需要刪除一個異常,那么你的方法簽名也需要改變,controller也隨之要改變,這又變成了緊耦合,這和用狀態碼123表示處理結果沒有什么不同。

    我們可以為每一種檢查項定義一個異常嗎?可以,但是那樣顯得太多余了。因為業務邏輯處理失敗的時候,根據我們需求,我們只需要通知用戶失敗的原因(通常應該是一段字符串),以及服務器受理失敗的一個狀態碼(有時可能不需要狀態碼,這要看你的設計了),這樣這需要一個包含原因屬性的異常即可滿足我們需求。

    最后我們決定這個異常繼承自RuntimeException。并且包含一個接受一個錯誤原因的構造器,這樣controller層也不需要知道異常,只要全局捕獲到ServiceException做統一的處理即可,這無論是在struct1,2時代,還是springMVC中,甚至servlet年代,都是極為容易的!

    異常不提供無參構造器,因為絕對不允許你拋出一個邏輯處理異常,但是不指明原因,想想看,你是必須要告訴用戶為什么受理失敗的!

    如此一來,我們只需要全局統一處理下 ServiceException 就可以了,很好,spring為我們提供了ControllerAdvice機制,有關ControllerAdvice,可以查閱springMVC使用文檔,下面是一個簡單的示例:

    在這個時候,我們就可以很輕松的處理各種情況了.

    注意一點,在這個類中,我們定義了2個log對象,分別指向 ServiceException.class 和 ModuleControllerAdvice.class。并且處理 ServiceException的時候使用了info級別的日志輸出,這是很有用的。

    • 首先,ServiceException一定要和其他的代碼錯誤分離,不應該混為一談.

    • 其次,ServiceException并不一定要記錄日志,我們應該提供獨立的log對象,方便開關.

    接下來你可以在修改用戶的時候想客戶端響應這樣的JSON

    如此一來沒有任何地方需要關心異常,或者業務邏輯校驗失敗的情況。用戶也可以得到很友好的錯誤提示。

    如何對異常進行分類

    如果你只需要一句概括,那么直接定義一個簡單的異常,用于中斷處理,并且與用戶保持友好交互即可。

    如果不可能一句話描述清楚,并且包含附加信息,比如需要在日志或者數據庫記錄消息ID,此時可能專門針對這種重要/復雜業務創建獨立異常。

    上述兩種情況因為web系統,是用戶發起請求之后需要等待程序給予響應結果的。

    如果是后臺作業,或者復雜業務需要追溯性。這種通常用流程判斷語句控制,要用異常處理。我們認為這些流程判斷一定在一個原子性處理中。并且檢查到(不是遇到)的問題(不是異常)需要記錄到用戶可友好查看的日志。這種情況屬于處理反饋,并不叫異常。

    綜上,筆者通常分為如下幾類:

  • 邏輯異常,這類異常用于描述業務無法按照預期的情況處理下去,屬于用戶制造的意外。

  • 代碼錯誤,這類異常用于描述開發的代碼錯誤,例如NPE,ILLARG,都屬于程序員制造的BUG。

  • 專有異常,多用于特定業務場景,用于描述指定作業出現意外情況無法預先處理。

  • 各類異常必須要有單獨的日志記錄,或者分級,分類可管理。有的時候僅僅想給三方運維看到邏輯異常。

    注意

    異常設計的初衷是解決程序運行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。

    上面這句話出自<java編程思想>,但是我們思考如下幾點:

    業務邏輯檢查,也是意外情況

    UnknownHostException,表示找不到這樣的主機,這個異常和NoUserException有什么區別么?換言之,沒有這樣的主機是異常,沒有這樣的用戶不是異常了么?所以一定要弄明白什么是用異常來控制邏輯,什么是定義程序異常。

    異常處理效率很低

    書中所示的例子,是在循環中大量使用try-catch進行檢查,但是業務系統,用戶發起請求的次數與該場景天壤地別。淘寶的11`11是個很好的反例。但是請你的系統上到這個級別再考慮這種問題。

  • 系統有千萬并發,不可能還去考慮這些中規中矩的按部就班的方式,別忘了MVC本來就浪費很多資源,代碼量增加很多。

  • 業務系統也存在很多巨量任務處理的情況。但是那些任務都是原子性的,現在MVC中的controller和service可不是原子性的,不然為什么要區分這么多層呢。

  • 如果那么在乎效率,考慮下重寫Throwable的fillStackTrace方法。你要知道異常的開銷大到底大在什么地方,fillStackTrace是一個native方法,會填充異常類內部的運行軌跡。

  • 不要用異常進行業務邏輯處理

    我們先來看一個例子:

    上述代碼就是典型的使用異常來處理業務邏輯。這種方式需要嚴重的禁止!上述代碼最大的問題在于,我們如何利用異常來自動處理事務呢?

    然而這和我們的異常中斷service沒有什么沖突.也并不是一回事.

    • 我們提倡在?業務處理?的時候,如果發現無法處理直接拋出異常即可。

    • 而并不是在?邏輯處理?的時候,用異常來判斷邏輯進行的狀況。

    改正后的邏輯

    ?

    ?

    總結

    以上是生活随笔為你收集整理的如何处理java异常的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。