<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          看看人家SpringBoot的全局異常處理多么優(yōu)雅...

          共 9857字,需瀏覽 20分鐘

           ·

          2020-12-02 02:22

          作者:虛無境

          https://www.cnblogs.com/xuwujing/p/10933082.html

          SpringBoot全局異常準(zhǔn)備

          說明:如果想直接獲取工程那么可以直接跳到底部,通過鏈接下載工程代碼。

          開發(fā)準(zhǔn)備

          環(huán)境要求JDK:1.8SpringBoot:1.5.17.RELEASE

          首先還是Maven的相關(guān)依賴:

          ??
          ????????UTF-8
          ????????1.8
          ????????1.8
          ????????1.8
          ????

          ????
          ????????org.springframework.boot
          ????????spring-boot-starter-parent
          ????????1.5.17.RELEASE
          ????????
          ????

          ????
          ????????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-web
          ????????

          ????????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-test
          ????????????test
          ????????


          ????????
          ????????????com.alibaba
          ????????????fastjson
          ????????????1.2.41
          ????????

          ????

          ????

          配置文件這塊基本不需要更改,全局異常的處理只需在代碼中實現(xiàn)即可。

          代碼編寫

          SpringBoot的項目已經(jīng)對有一定的異常處理了,但是對于我們開發(fā)者而言可能就不太合適了,因此我們需要對這些異常進(jìn)行統(tǒng)一的捕獲并處理。SpringBoot中有一個ControllerAdvice的注解,使用該注解表示開啟了全局異常的捕獲,我們只需在自定義一個方法使用ExceptionHandler注解然后定義捕獲異常的類型即可對這些捕獲的異常進(jìn)行統(tǒng)一的處理。

          我們根據(jù)下面的這個示例來看該注解是如何使用吧。

          示例代碼:

          @ControllerAdvice
          public?class?MyExceptionHandler?{

          ????@ExceptionHandler(value?=Exception.class)
          ?public?String?exceptionHandler(Exception?e){
          ??System.out.println("未知異常!原因是:"+e);
          ????????return?e.getMessage();
          ????}
          }

          上述的示例中,我們對捕獲的異常進(jìn)行簡單的二次處理,返回異常的信息,雖然這種能夠讓我們知道異常的原因,但是在很多的情況下來說,可能還是不夠人性化,不符合我們的要求。那么我們這里可以通過自定義的異常類以及枚舉類來實現(xiàn)我們想要的那種數(shù)據(jù)吧。

          自定義基礎(chǔ)接口類

          首先定義一個基礎(chǔ)的接口類,自定義的錯誤描述枚舉類需實現(xiàn)該接口。代碼如下:

          public?interface?BaseErrorInfoInterface?{
          ????/**?錯誤碼*/
          ??String?getResultCode();
          ?
          ?/**?錯誤描述*/
          ??String?getResultMsg();
          }

          自定義枚舉類

          然后我們這里在自定義一個枚舉類,并實現(xiàn)該接口。代碼如下:

          public?enum?CommonEnum?implements?BaseErrorInfoInterface?{
          ?//?數(shù)據(jù)操作錯誤定義
          ?SUCCESS("200",?"成功!"),?
          ?BODY_NOT_MATCH("400","請求的數(shù)據(jù)格式不符!"),
          ?SIGNATURE_NOT_MATCH("401","請求的數(shù)字簽名不匹配!"),
          ?NOT_FOUND("404",?"未找到該資源!"),?
          ?INTERNAL_SERVER_ERROR("500",?"服務(wù)器內(nèi)部錯誤!"),
          ?SERVER_BUSY("503","服務(wù)器正忙,請稍后再試!")
          ?;

          ?/**?錯誤碼?*/
          ?private?String?resultCode;

          ?/**?錯誤描述?*/
          ?private?String?resultMsg;

          ?CommonEnum(String?resultCode,?String?resultMsg)?{
          ??this.resultCode?=?resultCode;
          ??this.resultMsg?=?resultMsg;
          ?}

          ?@Override
          ?public?String?getResultCode()?{
          ??return?resultCode;
          ?}

          ?@Override
          ?public?String?getResultMsg()?{
          ??return?resultMsg;
          ?}

          }

          自定義異常類

          然后我們在來自定義一個異常類,用于處理我們發(fā)生的業(yè)務(wù)異常。代碼如下:

          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;
          ?}

          ?@Override
          ?public?Throwable?fillInStackTrace()?{
          ??return?this;
          ?}

          }

          自定義數(shù)據(jù)格式

          順便這里我們定義一下數(shù)據(jù)的傳輸格式。代碼如下:

          public?class?ResultBody?{
          ?/**
          ??*?響應(yīng)代碼
          ??*/
          ?private?String?code;

          ?/**
          ??*?響應(yīng)消息
          ??*/
          ?private?String?message;

          ?/**
          ??*?響應(yīng)結(jié)果
          ??*/
          ?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;
          ?}

          ?@Override
          ?public?String?toString()?{
          ??return?JSONObject.toJSONString(this);
          ?}

          }

          自定義全局異常處理類

          最后我們在來編寫一個自定義全局異常處理的類。代碼如下:

          @ControllerAdvice
          public?class?GlobalExceptionHandler?{
          ?private?static?final?Logger?logger?=?LoggerFactory.getLogger(GlobalExceptionHandler.class);
          ?
          ?/**
          ??*?處理自定義的業(yè)務(wù)異常
          ??*?@param?req
          ??*?@param?e
          ??*?@return
          ??*/
          ????@ExceptionHandler(value?=?BizException.class)??
          ????@ResponseBody??
          ?public??ResultBody?bizExceptionHandler(HttpServletRequest?req,?BizException?e){
          ?????logger.error("發(fā)生業(yè)務(wù)異常!原因是:{}",e.getErrorMsg());
          ?????return?ResultBody.error(e.getErrorCode(),e.getErrorMsg());
          ????}

          ?/**
          ??*?處理空指針的異常
          ??*?@param?req
          ??*?@param?e
          ??*?@return
          ??*/
          ?@ExceptionHandler(value?=NullPointerException.class)
          ?@ResponseBody
          ?public?ResultBody?exceptionHandler(HttpServletRequest?req,?NullPointerException?e){
          ??logger.error("發(fā)生空指針異常!原因是:",e);
          ??return?ResultBody.error(CommonEnum.BODY_NOT_MATCH);
          ?}


          ????/**
          ????????*?處理其他異常
          ?????*?@param?req
          ?????*?@param?e
          ?????*?@return
          ?????*/
          ????@ExceptionHandler(value?=Exception.class)
          ?@ResponseBody
          ?public?ResultBody?exceptionHandler(HttpServletRequest?req,?Exception?e){
          ?????logger.error("未知異常!原因是:",e);
          ????????return?ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);
          ????}
          }

          因為這里我們只是用于做全局異常處理的功能實現(xiàn)以及測試,所以這里我們只需在添加一個實體類和一個控制層類即可。

          實體類

          又是萬能的用戶表 (^▽^)

          代碼如下:

          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風(fēng)格實現(xiàn)的CRUD功能,不同的是這里我故意弄出了一些異常,好讓這些異常被捕獲到然后處理。這些異常中,有自定義的異常拋出,也有空指針的異常拋出,當(dāng)然也有不可預(yù)知的異常拋出(這里我用類型轉(zhuǎn)換異常代替),那么我們在完成代碼編寫之后,看看這些異常是否能夠被捕獲處理成功吧!

          代碼如下:

          @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("開始更新...");
          ???????//這里故意造成一個空指針的異常,并且不進(jìn)行處理
          ????????String?str=null;
          ????????str.equals("111");
          ????????return?true;
          ????}

          ????@DeleteMapping("/user")
          ????public?boolean?delete(@RequestBody?User?user)??{
          ????????System.out.println("開始刪除...");
          ????????//這里故意造成一個異常,并且不進(jìn)行處理
          ????????Integer.parseInt("abc123");
          ????????return?true;
          ????}

          ????@GetMapping("/user")
          ????public?List?findByUser(User?user)?{
          ?????System.out.println("開始查詢...");
          ????????List?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("程序正在運(yùn)行...");
          ????}
          }

          功能測試

          我們成功啟動該程序之后,使用Postman工具來進(jìn)行接口測試。

          首先進(jìn)行查詢,查看程序正常運(yùn)行是否ok,使用GET 方式進(jìn)行請求。

          GET http://localhost:8181/api/user

          返回參數(shù)為:

          {"id":1,"name":"xuwujing","age":18}

          示例圖:可以看到程序正常返回,并沒有因自定義的全局異常而影響。

          然后我們再來測試下自定義的異常是否能夠被正確的捕獲并處理。

          使用POST方式進(jìn)行請求

          POST http://localhost:8181/api/user

          Body參數(shù)為:

          {"id":1,"age":18}

          返回參數(shù)為:

          {"code":"-1","message":"用戶姓名不能為空!","result":null}

          示例圖:可以看出將我們拋出的異常進(jìn)行數(shù)據(jù)封裝,然后將異常返回出來。

          然后我們再來測試下空指針異常是否能夠被正確的捕獲并處理。在自定義全局異常中,我們除了定義空指針的異常處理,也定義最高級別之一的Exception異常,那么這里發(fā)生了空指針異常之后,它是回優(yōu)先使用哪一個呢?這里我們來測試下。

          使用PUT方式進(jìn)行請求。

          PUT http://localhost:8181/api/user

          Body參數(shù)為:

          {"id":1,"age":18}

          返回參數(shù)為:

          {"code":"400","message":"請求的數(shù)據(jù)格式不符!","result":null}

          示例圖:我們可以看到這里的的確是返回空指針的異常護(hù)理,可以得出全局異常處理優(yōu)先處理子類的異常。

          那么我們在來試試未指定其異常的處理,看該異常是否能夠被捕獲。

          使用DELETE方式進(jìn)行請求。

          DELETE http://localhost:8181/api/user

          Body參數(shù)為:

          {"id":1}

          返回參數(shù)為:

          {"code":"500","message":"服務(wù)器內(nèi)部錯誤!","result":null}

          這里可以看到它使用了我們在自定義全局異常處理類中的Exception異常處理的方法。到這里,測試就結(jié)束了。順便再說一下,自義定全局異常處理除了可以處理上述的數(shù)據(jù)格式之外,也可以處理頁面的跳轉(zhuǎn),只需在新增的異常方法的返回處理上填寫該跳轉(zhuǎn)的路徑并不使用ResponseBody 注解即可。細(xì)心的同學(xué)也許發(fā)現(xiàn)了在GlobalExceptionHandler類中使用的是ControllerAdvice注解,而非RestControllerAdvice注解,如果是用的RestControllerAdvice注解,它會將數(shù)據(jù)自動轉(zhuǎn)換成JSON格式,這種于ControllerRestController類似,所以我們在使用全局異常處理的之后可以進(jìn)行靈活的選擇處理。

          其它

          關(guān)于SpringBoot優(yōu)雅的全局異常處理的文章就講解到這里了,如有不妥,歡迎指正!

          項目地址

          SpringBoot全局異常的處理項目工程地址: https://github.com/xuwujing/springBoot-study/tree/master/springboot-exceptionHandler

          好文章,我在看

          瀏覽 60
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  a在线免费视频了 | 精品福利视频导航 | 蘑菇视频在线观看隐藏线路 | 97日日| av无码在线观看 国产精品欧美性爱 |