javascript
Spring Boot2.x-11 使用@ControllerAdvice和@ExceptionHandler实现自定义全局异常
文章目錄
- 概述
- 未使用全局異常且未顯式捕獲異常的情況
- 使用全局異常
- Step1. 自定義異常類
- Step2. 封裝異常信息模板
- Step3. 全局異常處理類
- Step4. 使用全局異常
- 小結
概述
我們在Spring Boot2.x-07Spring Boot2.1.2整合Mybatis這邊文章的基礎上來實現(xiàn)下Spring Boot使用@ControllerAdvice和@ExceptionHandler實現(xiàn)自定義全局異常。
首先需要明確的是:@ControllerAdvice 顧名思義主要處理的就是 controller 層的異常信息,沒有進入 controller 層的異常@ControllerAdvice 是無法處理的。 如果需要處理這種錯誤可以繼承BasicErrorController,可參考 https://segmentfault.com/a/1190000008443705
現(xiàn)在前后端分離的趨勢,前端通過ajax調用Restful接口,約定前后端的接口規(guī)范,后臺只需要按照約定格式返回JSON給前端即可,越來越少的項目會在Controller層糅合ModelAndView的信息了。
假定我們這里的項目是前后端分離,我們來探討下基于此種場景的全局異常處理(因此全局異常處理類我們使用了@RestControllerAdvice)
為什么需要全局異常呢?
-
Spring Boot 會將所有的異常發(fā)送到路徑為server.error.path(application.properties中可以配置,默認為”/error”)的控制器方法中進行處理,詳見BasicErrorController源碼 ,提示不友好
-
如果未使用全局異常的情況下,大量使用try-catch,難以閱讀,有些時候因為異常被try-catch捕獲導致@Transactional注解失效
比如我們之前寫的o2o的項目
Controller層充滿了大量的try-catch【不推薦使用try-catch,增大了代碼量,當異常過多對應的catch也就越多,不方便維護和擴展】,而且也只是簡單粗暴的返回Map<String, Object>,通過@ResponseBody轉換為JSON返回給前臺,非常不優(yōu)雅。
約定好返回格式+使用全局異常后,Controller層就清爽了很多,無需try-catch,并且還能避免因為異常被try-catch捕獲導致@Transactional注解失效。
未使用全局異常且未顯式捕獲異常的情況
我們先看下如果沒有全局異常,并且也沒有對異常進行捕獲,直接使用Spring Boot默認的異常顯示會怎樣呢?
先把個字段名故意寫錯來看下,
Controller層的代碼如下:
啟動Spring Boot工程,訪問下Controller層暴露的接口
http://localhost:8080/artisans
經(jīng)典的Whitelabel Error Page
如上圖,可以看到是非常的不友好,那這里我們來使用全局異常來改造下吧。
使用全局異常
Step1. 自定義異常類
項目中
package com.artisan.exception;import lombok.Getter;/*** 需要繼承RuntimeException。* 另外Spring 對于 RuntimeException類型的 異常才會進行事務回滾* @author yangshangwei**/ public class MyCustomException extends RuntimeException {private static final long serialVersionUID = 8863339790253662109L;@Getterprivate int code ;@Getterprivate String message;public MyCustomException() {super();}public MyCustomException(int code , String message) {this.message = message;this.code = code;}}Step2. 封裝異常信息模板
統(tǒng)一返回的異常信息的格式
package com.artisan.exception;import lombok.Getter;/*** 統(tǒng)一返回的異常信息的格式* * @author yangshangwei**/public class ExceptionResponseEntity {@Getterprivate int code;@Getterprivate String message;public ExceptionResponseEntity() {}public ExceptionResponseEntity(int code, String message) {this.code = code;this.message = message;}}Step3. 全局異常處理類
說明見代碼注釋
package com.artisan.exception;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;/*** * @ControllerAdvice 捕獲 Controller 層拋出的異常,如果添加 @ResponseBody 返回信息則為JSON 格式。@RestControllerAdvice 相當于 @ControllerAdvice 與 @ResponseBody 的結合體。@ExceptionHandler 統(tǒng)一處理一種類的異常,減少代碼重復率,降低復雜度。因為我們這里全部異常信息都約定返回json,所以直接使用 @RestControllerAdvice 代替 @ControllerAdvice ,這樣在方法上就可以不需要添加 @ResponseBody了步驟:1.創(chuàng)建一個 GlobalExceptionHandler 類,并添加上 @RestControllerAdvice 注解就可以實現(xiàn)異常通知類的定義了2.定義的方法中添加上 @ExceptionHandler 即可實現(xiàn)Controller層的異常捕捉**/@RestControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler{/*** 如果需要捕獲多個異常 定義如下:@ExceptionHandler({})* * @param request* @param e* @param response* @return*/// 捕獲多個異常的寫法@ExceptionHandler({MyCustomException.class,MyCustomException.class})public ExceptionResponseEntity customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());MyCustomException exception = (MyCustomException) e;logger.info("MyCustomException");return new ExceptionResponseEntity(exception.getCode(), exception.getMessage());}/*** 捕獲 RuntimeException 異常* 如果在一個 exceptionHandler 通過 if (e instanceof xxxException) 太麻煩,* 可以寫多個方法標注@ExceptionHandler處理不同的異常** @param request request* @param e exception* @param response response* @return 響應結果*/@ExceptionHandler(RuntimeException.class)public ExceptionResponseEntity runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());RuntimeException exception = (RuntimeException) e;logger.info("RuntimeException");return new ExceptionResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), exception.getMessage());}}通過 @ControllerAdvice(或者@RestControllerAdvice) 和 @ExceptionHandler 實現(xiàn)了對全局異常的捕獲。
這里僅定義了2個異常,一個是自定義的MyCustomException,另外一個是RuntimeException,按需增加自定義的異常類即可。
Step4. 使用全局異常
因為我們把數(shù)據(jù)庫字段寫錯了,所以這個方法肯定是拋出異常的,假定dao層和service層都未對異常進行處理,那么Controller層拋出的異常返回給前端是什么樣的呢?
啟動Spring Boot工程,
http://localhost:8080/artisans
結合控制臺輸出的日志
可知,GlobalExceptionHandler#runtimeExceptionHandler捕獲了該異常,而不是我們文章開始的那個經(jīng)典的Whitelabel Error Page頁面了
那我們剛才自定義的那個異常怎么來捕獲呢? 其實很簡單,只要throw即可
為了演示用法,我們修改下Controller層的方法 如下
訪問 http://localhost:8080/artisans
結合后臺日志可知 GlobalExceptionHandler#customExceptionHandler捕獲了該異常
小結
這里是使用@ControllerAdvice和@ExceptionHandler來實現(xiàn)全局的異常處理,其他方式比如使用AOP的方式也是可行的。 還有一種基于Spring Boot本身的全局異常統(tǒng)一處理,主要是實現(xiàn)ErrorController接口或者繼承AbstractErrorController抽象類或者繼承BasicErrorController類。
具體可參考這位大神的博客
https://blog.csdn.net/hao_kkkkk/article/details/80538955
總結
以上是生活随笔為你收集整理的Spring Boot2.x-11 使用@ControllerAdvice和@ExceptionHandler实现自定义全局异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot2.x-10 基于
- 下一篇: Spring Boot2.x-12 Sp