javascript
看看别人家 SpringBoot 的全局异常处理,多么优雅....
后端接口如何提高性能?
16 個寫代碼的好習慣
為什么不推薦使用BeanUtils屬性轉換工具
盤點阿里巴巴 34 個牛逼 GitHub 項目
來源:cnblogs.com/xuwujing/p/10933082.html
前言
本篇文章主要介紹的是SpringBoot項目進行全局異常的處理。
SpringBoot全局異常準備
說明:如果想直接獲取工程那么可以直接跳到底部,通過鏈接下載工程代碼。
開發準備
環境要求
JDK:1.8
SpringBoot:1.5.17.RELEASE
首先還是Maven的相關依賴:
??<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>1.8</java.version><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.17.RELEASE</version><relativePath?/></parent><dependencies><!--?Spring?Boot?Web?依賴?核心?--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--?Spring?Boot?Test?依賴?--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.41</version></dependency></dependencies>配置文件這塊基本不需要更改,全局異常的處理只需在代碼中實現即可。
代碼編寫
SpringBoot的項目已經對有一定的異常處理了,但是對于我們開發者而言可能就不太合適了,因此我們需要對這些異常進行統一的捕獲并處理。
SpringBoot中有一個ControllerAdvice的注解,使用該注解表示開啟了全局異常的捕獲,我們只需在自定義一個方法使用ExceptionHandler注解然后定義捕獲異常的類型即可對這些捕獲的異常進行統一的處理。
我們根據下面的這個示例來看該注解是如何使用吧。
示例代碼:
@ControllerAdvice public?class?MyExceptionHandler?{@ExceptionHandler(value?=Exception.class)public?String?exceptionHandler(Exception?e){System.out.println("未知異常!原因是:"+e);return?e.getMessage();} }上述的示例中,我們對捕獲的異常進行簡單的二次處理,返回異常的信息,雖然這種能夠讓我們知道異常的原因,但是在很多的情況下來說,可能還是不夠人性化,不符合我們的要求。
那么我們這里可以通過自定義的異常類以及枚舉類來實現我們想要的那種數據吧。
自定義基礎接口類
首先定義一個基礎的接口類,自定義的錯誤描述枚舉類需實現該接口。
代碼如下:
public?interface?BaseErrorInfoInterface?{/**?錯誤碼*/String?getResultCode();/**?錯誤描述*/String?getResultMsg(); }自定義枚舉類
然后我們這里在自定義一個枚舉類,并實現該接口。
代碼如下:
public?enum?CommonEnum?implements?BaseErrorInfoInterface?{//?數據操作錯誤定義SUCCESS("200",?"成功!"),?BODY_NOT_MATCH("400","請求的數據格式不符!"),SIGNATURE_NOT_MATCH("401","請求的數字簽名不匹配!"),NOT_FOUND("404",?"未找到該資源!"),?INTERNAL_SERVER_ERROR("500",?"服務器內部錯誤!"),SERVER_BUSY("503","服務器正忙,請稍后再試!");/**?錯誤碼?*/private?String?resultCode;/**?錯誤描述?*/private?String?resultMsg;CommonEnum(String?resultCode,?String?resultMsg)?{this.resultCode?=?resultCode;this.resultMsg?=?resultMsg;}@Overridepublic?String?getResultCode()?{return?resultCode;}@Overridepublic?String?getResultMsg()?{return?resultMsg;}}自定義異常類
然后我們在來自定義一個異常類,用于處理我們發生的業務異常。
代碼如下:
public?class?BizException?extends?RuntimeException?{private?static?final?long?serialVersionUID?=?1L;/***?錯誤碼*/protected?String?errorCode;/***?錯誤信息*/protected?String?errorMsg;public?BizException()?{super();}public?BizException(BaseErrorInfoInterface?errorInfoInterface)?{super(errorInfoInterface.getResultCode());this.errorCode?=?errorInfoInterface.getResultCode();this.errorMsg?=?errorInfoInterface.getResultMsg();}public?BizException(BaseErrorInfoInterface?errorInfoInterface,?Throwable?cause)?{super(errorInfoInterface.getResultCode(),?cause);this.errorCode?=?errorInfoInterface.getResultCode();this.errorMsg?=?errorInfoInterface.getResultMsg();}public?BizException(String?errorMsg)?{super(errorMsg);this.errorMsg?=?errorMsg;}public?BizException(String?errorCode,?String?errorMsg)?{super(errorCode);this.errorCode?=?errorCode;this.errorMsg?=?errorMsg;}public?BizException(String?errorCode,?String?errorMsg,?Throwable?cause)?{super(errorCode,?cause);this.errorCode?=?errorCode;this.errorMsg?=?errorMsg;}public?String?getErrorCode()?{return?errorCode;}public?void?setErrorCode(String?errorCode)?{this.errorCode?=?errorCode;}public?String?getErrorMsg()?{return?errorMsg;}public?void?setErrorMsg(String?errorMsg)?{this.errorMsg?=?errorMsg;}public?String?getMessage()?{return?errorMsg;}@Overridepublic?Throwable?fillInStackTrace()?{return?this;}}自定義數據格式
順便這里我們定義一下數據的傳輸格式。
代碼如下:
public?class?ResultBody?{/***?響應代碼*/private?String?code;/***?響應消息*/private?String?message;/***?響應結果*/private?Object?result;public?ResultBody()?{}public?ResultBody(BaseErrorInfoInterface?errorInfo)?{this.code?=?errorInfo.getResultCode();this.message?=?errorInfo.getResultMsg();}public?String?getCode()?{return?code;}public?void?setCode(String?code)?{this.code?=?code;}public?String?getMessage()?{return?message;}public?void?setMessage(String?message)?{this.message?=?message;}public?Object?getResult()?{return?result;}public?void?setResult(Object?result)?{this.result?=?result;}/***?成功*?*?@return*/public?static?ResultBody?success()?{return?success(null);}/***?成功*?@param?data*?@return*/public?static?ResultBody?success(Object?data)?{ResultBody?rb?=?new?ResultBody();rb.setCode(CommonEnum.SUCCESS.getResultCode());rb.setMessage(CommonEnum.SUCCESS.getResultMsg());rb.setResult(data);return?rb;}/***?失敗*/public?static?ResultBody?error(BaseErrorInfoInterface?errorInfo)?{ResultBody?rb?=?new?ResultBody();rb.setCode(errorInfo.getResultCode());rb.setMessage(errorInfo.getResultMsg());rb.setResult(null);return?rb;}/***?失敗*/public?static?ResultBody?error(String?code,?String?message)?{ResultBody?rb?=?new?ResultBody();rb.setCode(code);rb.setMessage(message);rb.setResult(null);return?rb;}/***?失敗*/public?static?ResultBody?error(?String?message)?{ResultBody?rb?=?new?ResultBody();rb.setCode("-1");rb.setMessage(message);rb.setResult(null);return?rb;}@Overridepublic?String?toString()?{return?JSONObject.toJSONString(this);}}自定義全局異常處理類
最后我們在來編寫一個自定義全局異常處理的類。
代碼如下:
@ControllerAdvice public?class?GlobalExceptionHandler?{private?static?final?Logger?logger?=?LoggerFactory.getLogger(GlobalExceptionHandler.class);/***?處理自定義的業務異常*?@param?req*?@param?e*?@return*/@ExceptionHandler(value?=?BizException.class)??@ResponseBody??public??ResultBody?bizExceptionHandler(HttpServletRequest?req,?BizException?e){logger.error("發生業務異常!原因是:{}",e.getErrorMsg());return?ResultBody.error(e.getErrorCode(),e.getErrorMsg());}/***?處理空指針的異常*?@param?req*?@param?e*?@return*/@ExceptionHandler(value?=NullPointerException.class)@ResponseBodypublic?ResultBody?exceptionHandler(HttpServletRequest?req,?NullPointerException?e){logger.error("發生空指針異常!原因是:",e);return?ResultBody.error(CommonEnum.BODY_NOT_MATCH);}/***?處理其他異常*?@param?req*?@param?e*?@return*/@ExceptionHandler(value?=Exception.class)@ResponseBodypublic?ResultBody?exceptionHandler(HttpServletRequest?req,?Exception?e){logger.error("未知異常!原因是:",e);return?ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);} }因為這里我們只是用于做全局異常處理的功能實現以及測試,所以這里我們只需在添加一個實體類和一個控制層類即可。
實體類
又是萬能的用戶表 (▽)
代碼如下:
public?class?User?implements?Serializable{private?static?final?long?serialVersionUID?=?1L;/**?編號?*/private?int?id;/**?姓名?*/private?String?name;/**?年齡?*/private?int?age;public?User(){}public?int?getId()?{return?id;}public?void?setId(int?id)?{this.id?=?id;}public?String?getName()?{return?name;}public?void?setName(String?name)?{this.name?=?name;}public?int?getAge()?{return?age;}public?void?setAge(int?age)?{this.age?=?age;}public?String?toString()?{return?JSONObject.toJSONString(this);} }Controller 控制層
控制層這邊也比較簡單,使用Restful風格實現的CRUD功能,不同的是這里我故意弄出了一些異常,好讓這些異常被捕獲到然后處理。
這些異常中,有自定義的異常拋出,也有空指針的異常拋出,當然也有不可預知的異常拋出(這里我用類型轉換異常代替),那么我們在完成代碼編寫之后,看看這些異常是否能夠被捕獲處理成功吧!
代碼如下:
@RestController @RequestMapping(value?=?"/api") public?class?UserRestController?{@PostMapping("/user")public?boolean?insert(@RequestBody?User?user)?{System.out.println("開始新增...");//如果姓名為空就手動拋出一個自定義的異常!if(user.getName()==null){throw??new?BizException("-1","用戶姓名不能為空!");}return?true;}@PutMapping("/user")public?boolean?update(@RequestBody?User?user)?{System.out.println("開始更新...");//這里故意造成一個空指針的異常,并且不進行處理String?str=null;str.equals("111");return?true;}@DeleteMapping("/user")public?boolean?delete(@RequestBody?User?user)??{System.out.println("開始刪除...");//這里故意造成一個異常,并且不進行處理Integer.parseInt("abc123");return?true;}@GetMapping("/user")public?List<User>?findByUser(User?user)?{System.out.println("開始查詢...");List<User>?userList?=new?ArrayList<>();User?user2=new?User();user2.setId(1L);user2.setName("xuwujing");user2.setAge(18);userList.add(user2);return?userList;}}App 入口
和普通的SpringBoot項目基本一樣。
代碼如下:
@SpringBootApplication public?class?App? {public?static?void?main(?String[]?args?){SpringApplication.run(App.class,?args);System.out.println("程序正在運行...");} }功能測試
我們成功啟動該程序之后,使用Postman工具來進行接口測試。
首先進行查詢,查看程序正常運行是否ok,使用GET 方式進行請求。
GET http://localhost:8181/api/user
返回參數為:
{"id":1,"name":"xuwujing","age":18}示例圖:
可以看到程序正常返回,并沒有因自定義的全局異常而影響。
然后我們再來測試下自定義的異常是否能夠被正確的捕獲并處理。
使用POST方式進行請求
POST http://localhost:8181/api/user
Body參數為:
{"id":1,"age":18}返回參數為:
{"code":"-1","message":"用戶姓名不能為空!","result":null}示例圖:
可以看出將我們拋出的異常進行數據封裝,然后將異常返回出來。
然后我們再來測試下空指針異常是否能夠被正確的捕獲并處理。在自定義全局異常中,我們除了定義空指針的異常處理,也定義最高級別之一的Exception異常,那么這里發生了空指針異常之后,它是回優先使用哪一個呢?這里我們來測試下。
使用PUT方式進行請求。
PUT http://localhost:8181/api/user
Body參數為:
{"id":1,"age":18}返回參數為:
{"code":"400","message":"請求的數據格式不符!","result":null}示例圖:
我們可以看到這里的的確是返回空指針的異常護理,可以得出全局異常處理優先處理子類的異常。
那么我們在來試試未指定其異常的處理,看該異常是否能夠被捕獲。
使用DELETE方式進行請求。
DELETE http://localhost:8181/api/user
Body參數為:
{"id":1}返回參數為:
{"code":"500","message":"服務器內部錯誤!","result":null}這里可以看到它使用了我們在自定義全局異常處理類中的Exception異常處理的方法。
到這里,測試就結束了。順便再說一下,自義定全局異常處理除了可以處理上述的數據格式之外,也可以處理頁面的跳轉,只需在新增的異常方法的返回處理上填寫該跳轉的路徑并不使用ResponseBody 注解即可。
細心的同學也許發現了在GlobalExceptionHandler類中使用的是ControllerAdvice注解,而非RestControllerAdvice注解,如果是用的RestControllerAdvice注解,它會將數據自動轉換成JSON格式,這種于Controller和RestController類似,所以我們在使用全局異常處理的之后可以進行靈活的選擇處理。
其它
關于SpringBoot優雅的全局異常處理的文章就講解到這里了,如有不妥,歡迎指正!
項目地址
SpringBoot全局異常的處理項目工程地址:
https://github.com/xuwujing/springBoot-study/tree/master/springboot-exceptionHandler
推薦文章14個項目!
一款小清新的 SpringBoot+ Mybatis 前后端分離后臺管理系統項目
47K Star 的SpringBoot+MyBatis+docker電商項目,附帶超詳細的文檔!
寫博客能月入10K?
一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)
這或許是最美的Vue+Element開源后臺管理UI
推薦一款高顏值的 Spring Boot 快速開發框架
一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)
13K點贊都基于 Vue+Spring 前后端分離管理系統ELAdmin,大愛
想接私活時薪再翻一倍,建議根據這幾個開源的SpringBoot
總結
以上是生活随笔為你收集整理的看看别人家 SpringBoot 的全局异常处理,多么优雅....的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 推荐一款Java开发的精美个人博客
- 下一篇: 用鸿蒙跑了个 hello world