SpringBoot項(xiàng)目實(shí)現(xiàn)統(tǒng)一異常處理的最佳方案
前言
近日心血來潮想做一個開源項(xiàng)目,目標(biāo)是做一款可以適配多端、功能完備的模板工程,包含后臺管理系統(tǒng)和前臺系統(tǒng),開發(fā)者基于此項(xiàng)目進(jìn)行裁剪和擴(kuò)展來完成自己的功能開發(fā)。本項(xiàng)目為前后端分離開發(fā),后端基于Java21
和SpringBoot3
開發(fā),后端使用Spring Security
、JWT
、Spring Data JPA
等技術(shù)棧,前端提供了vue
、angular
、react
、uniapp
、微信小程序
等多種腳手架工程。
項(xiàng)目地址:https://gitee.com/breezefaith/fast-alden
在前后端分離的項(xiàng)目開發(fā)過程中,我們通常會對數(shù)據(jù)返回格式進(jìn)行統(tǒng)一的處理,這樣可以方便前端人員取數(shù)據(jù),后端發(fā)生異常時同樣會使用此格式將異常信息返回給前端。本文將介紹在SpringBoot項(xiàng)目中如何實(shí)現(xiàn)統(tǒng)一異常處理。
實(shí)現(xiàn)步驟
定義統(tǒng)一響應(yīng)對象類
/** * 響應(yīng)結(jié)果類 * * @param <T> 任意類型 */ @Data public class ResponseResult<T> { /** * 響應(yīng)狀態(tài)碼,200是正常,非200表示異常 */ private int status; /** * 異常編號 */ private String errorCode; /** * 異常信息 */ private String message; /** * 響應(yīng)數(shù)據(jù) */ private T data; public static <T> ResponseResult<T> success() { return success(HttpServletResponse.SC_OK, null, null); } public static <T> ResponseResult<T> success(T data) { return success(HttpServletResponse.SC_OK, null, data); } public static <T> ResponseResult<T> fail(String message) { return fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, message, null); } public static <T> ResponseResult<T> fail(String errorCode, String message) { return fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorCode, message, null); } public static <T> ResponseResult<T> success(int status, String message, T data) { ResponseResult<T> r = new ResponseResult<>(); r.setStatus(status); r.setMessage(message); r.setData(data); return r; } public static <T> ResponseResult<T> fail(int status, String errorCode, String message) { return fail(status, errorCode, message, null); } public static <T> ResponseResult<T> fail(int status, String errorCode, String message, T data) { ResponseResult<T> r = new ResponseResult<>(); r.setStatus(status); r.setErrorCode(errorCode); r.setMessage(message); r.setData(data); return r; } }
定義業(yè)務(wù)異常枚舉接口和實(shí)現(xiàn)
通常一個系統(tǒng)中的自定義業(yè)務(wù)異常是可窮舉的,可以考慮通過定義枚舉的方式來列舉所有的業(yè)務(wù)異常。
首先我們來定義一個異常信息枚舉的基類接口。
public interface IBizExceptionEnum { String getCode(); String getMessage(); }
再給出一個常用的異常信息的枚舉類,如果有其他業(yè)務(wù)模塊的異常信息,同樣可以通過實(shí)現(xiàn)IBizExceptionEnum
接口來進(jìn)行定義。
@Getter public enum BizExceptionEnum implements IBizExceptionEnum { ENTITY_IS_NULL("Base_Entity_Exception_0001", "實(shí)體為空"), ENTITY_ID_IS_NULL("Base_Entity_Exception_0002", "實(shí)體id字段為空"), ENTITY_ID_IS_DUPLCATED("Base_Entity_Exception_0003", "實(shí)體id字段%s重復(fù)"); private final String code; private final String message; BizExceptionEnum(String code, String message) { this.code = code; this.message = message; } }
定義業(yè)務(wù)異?;?/h3>
業(yè)務(wù)異?;?code>BizException繼承自RuntimeException
,代碼中主動拋出的異常建議都包裝為該類的實(shí)例。
/** * 業(yè)務(wù)異常基類,支持參數(shù)化的異常信息 */ @Getter @Setter public class BizException extends RuntimeException { private String code; private Object[] args; public BizException() { super(); } public BizException(String message) { super(message); } public BizException(Throwable cause) { super(cause); } public BizException(String message, Throwable cause) { super(message, cause); } public BizException(Throwable cause, String code, String message, Object... args) { super(message, cause); this.code = code; this.args = args; } public BizException(String code, String message, Object... args) { super(message); this.code = code; this.args = args; } public BizException(IBizExceptionEnum exceptionEnum, Object... args) { this(exceptionEnum.getCode(), exceptionEnum.getMessage(), args); } public BizException(Throwable cause, IBizExceptionEnum exceptionEnum, Object... args) { this(cause, exceptionEnum.getCode(), exceptionEnum.getMessage(), args); } @Override public String getMessage() { if (code != null) { if (args != null && args.length > 0) { return String.format(super.getMessage(), args); } } return super.getMessage(); } }
定義全局異常處理切面
本步驟需要使用@RestControllerAdvice
注解,它是一個組合注解,由@ControllerAdvice
、@ResponseBody
組成,而@ControllerAdvice
繼承了@Component
,因此@RestControllerAdvice
本質(zhì)上是個Component
,用于定義@ExceptionHandler
,@InitBinder
和@ModelAttribute
方法,適用于所有使用@RequestMapping
方法。
還要用到@ExceptionHandler
注解,可以認(rèn)為它是一個異常攔截器,它采用“就近原則”,存在多個滿足條件的異常處理器時會選擇最接近的一個來使用。它本質(zhì)上就是使用Spring AOP定義的一個切面,在系統(tǒng)拋出異常后執(zhí)行。
具體實(shí)現(xiàn)代碼如下:
/** * 全局異常處理切面 */ @RestControllerAdvice public class GlobalExceptionHandlerAdvice { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandlerAdvice.class); @ExceptionHandler({BizException.class}) public ResponseResult<Object> handleBizException(BizException e, HttpServletRequest request, HttpServletResponse response) { log.error(e.getCode() + ": " + e.getMessage(), e); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return ResponseResult.fail(e.getCode(), e.getMessage()); } @ExceptionHandler({RuntimeException.class, Exception.class}) public ResponseResult<Object> handleRuntimeException(Exception e, HttpServletRequest request, HttpServletResponse response) { log.error(e.getMessage(), e); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return ResponseResult.fail(e.getMessage()); } }
上述代碼會對系統(tǒng)中拋出的BizException
、RuntimeException
和Exception
對象進(jìn)行處理,把異常包裝為ResponseResult
對象后將異常編號和異常信息返回給前端。
測試和驗(yàn)證
下面我們就可以定義一個Controller類來進(jìn)行簡單的測試。
@RestController @RequestMapping("/demo") public class DemoController { @GetMapping("/method1") public ResponseResult<Integer> method1() { throw new BizException(BizExceptionEnum.ENTITY_IS_NULL); } @GetMapping("/method2") public void method2() { throw new BizException(BizExceptionEnum.ENTITY_ID_IS_NULL); } @GetMapping(value = "/method3") public String method3() { throw new BizException(BizExceptionEnum.ENTITY_ID_IS_DUPLCATED, "1"); } @GetMapping(value = "/method4") public String method4() { // 拋出ArithmeticException異常 return String.valueOf(1 / 0); } }
總結(jié)
本文介紹了如何在SpringBoot項(xiàng)目中實(shí)現(xiàn)統(tǒng)一異常處理,如有錯誤,還望批評指正。
在后續(xù)實(shí)踐中我也是及時更新自己的學(xué)習(xí)心得和經(jīng)驗(yàn)總結(jié),希望與諸位看官一起進(jìn)步。
到此這篇關(guān)于SpringBoot項(xiàng)目實(shí)現(xiàn)統(tǒng)一異常處理的最佳方案的文章就介紹到這了,更多相關(guān)SpringBoot統(tǒng)一異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jstack+jdb命令查看線程及死鎖堆棧信息的實(shí)例
這篇文章主要介紹了jstack+jdb命令查看線程及死鎖堆棧信息的實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Javaweb 500 服務(wù)器內(nèi)部錯誤的解決
這篇文章主要介紹了Javaweb 500 服務(wù)器內(nèi)部錯誤的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09實(shí)例講解String Date Calendar之間的轉(zhuǎn)換
下面小編就為大家?guī)硪黄獙?shí)例講解String Date Calendar之間的轉(zhuǎn)換。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07JAVA中的deflate壓縮實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄狫AVA中的deflate壓縮實(shí)現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09SpringBoot實(shí)現(xiàn)excel文件生成和下載
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)excel文件生成和下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-02-02