Spring Boot 全域性異常處理(下)

可以搜尋微信公眾號【Jet 與程式設計】檢視更多精彩文章

Spring Boot 全域性異常處理(下)

背景

在上篇【連結】中介紹了 Spring Boot 全域性異常處理的一種方式,但那是一種全域性性的容錯機制,目的是把 Spring Boot 預設的 ErrorController 替換掉,從而更友好地展示自定義的異常資訊給客戶端。

當然,除了這種全域性的容錯機制,有時候我們更希望對 Controller 層指定的異常進行特殊的處理,更甚至說這是異常處理的一種標準手段,即業務層的異常直接向上拋即可,不用自己處理,然後由 Controller 層進行統一的異常處理。

說明:本文的全域性異常處理,同樣可以抽象地理解成:全域性處理 Controller 層丟擲的異常

實現

Spring 給我們提供的兩種實現的方式:

區域性異常處理:

@Controller + @ExceptionHandler

全域性異常處理:

@ControllerAdvice + @ExceptionHandler

此處的區域性和全域性的區別在於:

當代理方法出現在 @Controller 註解的類中時,則此方法會代理此 Controller 中的異常。

當代理方法出現在 @ControllerAdvice 註解的類中時,則此方法會代理所有 Controller 中的異常。

區域性異常處理

區域性異常處理,我們只需要在 @Controller 註解的類中寫一個自定義的異常處理方法,然後給這個方法新增 @ExceptionHandler 註解即可,效果就是當此類中的方法有未捕獲的異常丟擲時,異常會以引數的形式傳遞給這個異常處理方法,我們只需要在此方法中進行相關的異常處理即可。

//區域性異常處理(ps:對於引數必填的400異常也會被此異常處理器捕獲)@ExceptionHandler(Exception。class)public String exHandler(Exception e) { if(e instanceof ArithmeticException) { return “除0異常-區域性捕獲”; } if(e instanceof STCRException) { return “自定義異常-區域性捕獲”; } // 未知的異常做出響應 return “未知異常-區域性捕獲”;}

上述方法會處理所有的異常(因為引數是 Exception),當然,

我們也可以根據異常的不同,來編寫多個方法分別處理對應的異常

(實現的過程只需要引數傳遞不同的異常型別即可)。

但是我們不可能在所有的類中都寫一遍異常的處理方法,所以此處有兩個思路:

思路一

:抽取異常處理程式碼做成基類,然後由每個需要異常處理的 Controller 類去繼承

缺點

:類的繼承機制,其實是一種概念上的屬性的繼承,此處僅僅是為了獲得一個公共方法而去做異常類的繼承,顯然破壞了程式碼的優雅性。再言之,類是隻能繼承一次的,這樣做有可能會破壞程式碼結構的設計。

思路二

:抽取程式碼做成介面,然後由每個需要異常處理的 Controller 類去實現這個介面即可

這種思路得益於 Java8 的 Functional Interface,即可用在介面中寫一個預設方法,此方法中是可以寫具體的實現的。

缺點

:對環境的要求有些苛刻,即要求 JDK 的版本至少為 1。8,在某些應用中,顯然是不合適的。

最後,無論是哪種抽取基類的實現,都是存在侷限性的,因為這都要求我們去繼承或實現該基類,顯然這不是我們想要的,我們想要的是那種一勞永逸的實現方式。

全域性異常處理

全域性異常處理,其實很簡單,我們只需要寫一個配置類,在該類上添加註解

@ControllerAdvice

,然後在該類中異常處理方法即可,同上文的區域性異常處理方法。

效果是該類中的異常處理方法會自動處理所有 Controller 中丟擲的異常,這才是滿足我們需求的異常處理方式。

@ControllerAdvicepublic class GlobalExceptionHandler { //處理自定義的異常 @ExceptionHandler(STCRException。class) @ResponseBody public Object customHandler(STCRException e) { return “自定義異常-全域性”; } //其他未處理的異常 @ExceptionHandler(Exception。class) @ResponseBody public Object exceptionHandler(Exception e) { return “其它異常-全域性”; }}

總結

好了,我們先整理一下異常的處理流程,最外層的異常處理由 Spring 的全域性異常 ErrorController 來接管,這其實是在過濾器層面進行的異常處理,內層由 @ExceptionHandler 處理所有 Controller 丟擲的異常。

比如 404 異常,尚未進入 Controller 層,所以異常由 ErrorController 捕獲處理。

比如 必填的引數缺失(400)異常,因為已經進入了 Controller 層,所有異常由 ExceptionHandler 捕獲處理。

注:區域性異常的優先順序是高於全域性異常的