前言
在平時(shí)的 API 開(kāi)發(fā)過(guò)程中,總會(huì)遇到一些錯(cuò)誤異常沒(méi)有捕捉到的情況。那有的小伙伴可能會(huì)想,這還不簡(jiǎn)單么,我在 API 最外層加一個(gè) try...catch
不就完事了。
哈哈哈,沒(méi)錯(cuò)。這種方法簡(jiǎn)單粗暴。小編曾經(jīng)也是這么干的,但是你轉(zhuǎn)過(guò)來(lái)想一想,你會(huì)在每一個(gè) API 入口,都去做 try...catch
嗎?這樣不是代碼非常丑陋的。小伙伴開(kāi)始思考,突然靈光一現(xiàn),說(shuō)我們實(shí)現(xiàn)一個(gè) AOP 來(lái)做這事不就完了。沒(méi)錯(cuò),使用 AOP 來(lái)實(shí)現(xiàn)是最佳的選擇。
現(xiàn)在就給大家來(lái)介紹介紹 Spring Boot
怎么通過(guò)注解來(lái)實(shí)現(xiàn)全局異常處理的。
主角 @ControllerAdvice
和 @ExceptionHandler
我們先來(lái)介紹一下今天的主角,分別是 @ControllerAdvice
和 @ExceptionHandler
。
@ControllerAdvice
相當(dāng)于controller
的切面,主要用于@ExceptionHandler
,@InitBinder
和@ModelAttribute
,使注解標(biāo)注的方法對(duì)每一個(gè)controller
都起作用。默認(rèn)對(duì)所有controller
都起作用,當(dāng)然也可以通過(guò)@ControllerAdvice
注解中的一些屬性選定符合條件的controller
。@ExceptionHandler
用于異常處理的注解,可以通過(guò)value
指定處理哪種類(lèi)型的異常還可以與@ResponseStatus
搭配使用,處理特定的http
錯(cuò)誤。標(biāo)記的方法入?yún)⑴c返回值都有很大的靈活性,具體可以看注釋也可以在后邊的深度探究。
案例分析
今天我們就通過(guò)幾種案例的方式,來(lái)給大家分析分析,怎么通過(guò)全局異常處理的方式玩轉(zhuǎn) Spring Boot 的全局異常處理。
案例一
一般的異常處理,所有的API都需要有相同的異常結(jié)構(gòu)。
exception1
在這種情況下,實(shí)現(xiàn)是非常簡(jiǎn)單的,我們只需要?jiǎng)?chuàng)建 GeneralExceptionHandler
類(lèi),用 @ControllerAdvice
注解來(lái)注解它,并創(chuàng)建所需的 @ExceptionHandler
,它將處理所有由應(yīng)用程序拋出的異常,如果它能找到匹配的 @ExceptionHandler
,它將相應(yīng)地進(jìn)行轉(zhuǎn)換。
@ControllerAdvice
public class GeneralExceptionHandler {
@ExceptionHandler(Exception.class)
protected ResponseEntity< Error > handleException(Exception ex) {
MyError myError = MyError.builder()
.text(ex.getMessage())
.code(ex.getErrorCode()).build();
return new ResponseEntity(myError,
HttpStatus.valueOf(ex.getErrorCode()));
}
}
案例二
我們有一個(gè)API,它需要有一個(gè)或多個(gè)異常以其他格式處理,與其他應(yīng)用程序的 API 不同。
exception2
我們可以采取兩種方式來(lái)實(shí)現(xiàn)這種情況。我們可以在 OtherController
內(nèi)部添加 @ExceptionHandler
來(lái)處理 OtherException
,或者為 OtherController
創(chuàng)建新的@ControllerAdvice
,以備我們也想在其他 API 中處理 OtherException
。
在 OtherController
中添加 @ExceptionHandler
來(lái)處理 OtherException
的代碼示例。
@RestController
@RequestMapping("/other")
public class OtherController {
@ExceptionHandler(OtherException.class)
protected ResponseEntity< Error > handleException(OtherException ex) {
MyOtherError myOtherError = MyOtherError.builder()
.message(ex.getMessage())
.origin("Other API")
.code(ex.getErrorCode()).build();
return new ResponseEntity(myOtherError,
HttpStatus.valueOf(ex.getErrorCode()));
}
}
只針對(duì) OtherController
控制器的 @ControllerAdvice
的代碼示例
@ControllerAdvice(assignableTypes = OtherController.class)
public class OtherExceptionHandler {
@ExceptionHandler(OtherException.class)
protected ResponseEntity< Error > handleException(OtherException ex) {
MyOtherError myOtherError = MyOtherError.builder()
.message(ex.getMessage())
.origin("Other API")
.code(ex.getErrorCode()).build();
return new ResponseEntity(myOtherError,
HttpStatus.valueOf(ex.getErrorCode()));
}
}
案例三
與案例二類(lèi)似,我們有一個(gè) API 需要以不同于應(yīng)用程序中其他 API 的方式對(duì)異常進(jìn)行格式化,但這次所有的異常都需要進(jìn)行不同的轉(zhuǎn)換。
exception3
為了實(shí)現(xiàn)這個(gè)案例,我們將不得不使用兩個(gè) @ControllerAdvice
,并加上 @Order
注解的注意事項(xiàng)。因?yàn)楝F(xiàn)在我們需要告訴 Spring
,在處理同一個(gè)異常時(shí),哪個(gè) @ControllerAdvice
的優(yōu)先級(jí)更高。如果我們沒(méi)有指定 @Order
,在啟動(dòng)時(shí),其中一個(gè)處理程序?qū)⒆詣?dòng)注冊(cè)為更高的順序,我們的異常處理將變得不可預(yù)測(cè)。例如,我最近看到一個(gè)案例,如果你使用 mvn springboot:run
任務(wù)啟動(dòng)一個(gè)應(yīng)用程序,OtherExceptionHandler
是主要的,但是當(dāng)以jar形式啟動(dòng)時(shí),GeneralExceptionHandler
是主要的。
@ControllerAdvice
public class GeneralExceptionHandler {
@ExceptionHandler(Exception.class)
protected ResponseEntity< Error > handleException(Exception ex) {
MyError myError = MyError.builder()
.text(ex.getMessage())
.code(ex.getErrorCode()).build();
return new ResponseEntity(myError,
HttpStatus.valueOf(ex.getErrorCode()));
}
}
@ControllerAdvice(assignableTypes = OtherController.class)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OtherExceptionHandler {
@ExceptionHandler(Exception.class)
protected ResponseEntity< Error > handleException(Exception ex) {
MyError myError = MyError.builder()
.message(ex.getMessage())
.origin("Other API")
.code(ex.getErrorCode()).build();
return new ResponseEntity(myError,
HttpStatus.valueOf(ex.getErrorCode()));
}
}
總結(jié)
經(jīng)過(guò)上述的幾個(gè)案例,指北君覺(jué)得大家應(yīng)該已經(jīng)能夠輕松應(yīng)對(duì) Spring Boot 中大部分的全局異常處理的情況。
細(xì)心的同學(xué)也許會(huì)覺(jué)得為什么不使用 @RestControllerAdvice
呢?如果是用的 @RestControllerAdvice
注解,它會(huì)將數(shù)據(jù)自動(dòng)轉(zhuǎn)換成JSON格式,不再需要 ResponseEntity
的處理來(lái)。這種與 Controller
和 RestController
類(lèi)似,本質(zhì)是一樣的,所以我們?cè)谑褂萌之惓L幚碇罂梢赃M(jìn)行靈活的選擇處理。
-
API
+關(guān)注
關(guān)注
2文章
1511瀏覽量
62399 -
代碼
+關(guān)注
關(guān)注
30文章
4827瀏覽量
69055 -
SpringBoot
+關(guān)注
關(guān)注
0文章
174瀏覽量
201
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
Spring Boot如何實(shí)現(xiàn)異步任務(wù)
啟動(dòng)Spring Boot項(xiàng)目應(yīng)用的三種方法
使用Spring自定義注解的實(shí)現(xiàn)
Spring Boot框架錯(cuò)誤處理
Spring Boot 系列(八)@ControllerAdvice 攔截異常并統(tǒng)一處理
Spring Boot Web相關(guān)的基礎(chǔ)知識(shí)
簡(jiǎn)述Spring Boot數(shù)據(jù)校驗(yàn)
Spring Web MVC注解
![<b class='flag-5'>Spring</b> Web MVC<b class='flag-5'>注解</b>](https://file1.elecfans.com/web2/M00/81/FF/wKgaomQvjouAEB3mAAEJjN1OpNQ018.jpg)
Spring Bean相關(guān)的4個(gè)注解及使用方法
![<b class='flag-5'>Spring</b> Bean相關(guān)的4個(gè)<b class='flag-5'>注解</b>及使用方法](https://file1.elecfans.com/web2/M00/81/FF/wKgaomQvjviAWyDeAADCHy-wd84212.jpg)
容器配置及Spring Boot注解
![容器配置及<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b><b class='flag-5'>注解</b>](https://file.elecfans.com/web2/M00/9D/5E/poYBAGQvkDCAH2yDAABjOkpwCO8436.png)
Spring中@Component注解是怎么實(shí)現(xiàn)的
![<b class='flag-5'>Spring</b>中@Component<b class='flag-5'>注解</b>是怎么<b class='flag-5'>實(shí)現(xiàn)</b>的](https://file.elecfans.com/web2/M00/9D/F3/pYYBAGQvxC2ADEcoAAAxhbkime4164.png)
Spring Boot啟動(dòng) Eureka流程
![<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>啟動(dòng) Eureka流程](https://file1.elecfans.com/web2/M00/A7/86/wKgaomUkx7OAdMOGAAIBWIj8ao0506.jpg)
Spring Boot的啟動(dòng)原理
![<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>的啟動(dòng)原理](https://file1.elecfans.com/web2/M00/A9/C0/wKgZomUovNCAdZmWAADhZidr2zI277.jpg)
Spring Boot 的設(shè)計(jì)目標(biāo)
![<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> 的設(shè)計(jì)目標(biāo)](https://file1.elecfans.com/web2/M00/A8/04/wKgaomUo6gmAEQN0AAESfDw9EW8049.jpg)
評(píng)論