<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)雅...

          共 17633字,需瀏覽 36分鐘

           ·

          2021-05-14 20:30


          轉(zhuǎn)自:xuwujing

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


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

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

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

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

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

            <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 依賴(lài) 核心 -->
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>
                  <!-- Spring Boot Test 依賴(lài) -->
                  <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>
              

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

          代碼編寫(xiě)

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

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

          示例代碼:

          @ControllerAdvice
          public class MyExceptionHandler {

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

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

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

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

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

          自定義枚舉類(lèi)

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

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

           /** 錯(cuò)誤碼 */
           private String resultCode;

           /** 錯(cuò)誤描述 */
           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;
           }

          }

          自定義異常類(lèi)

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

          public class BizException extends RuntimeException {

           private static final long serialVersionUID = 1L;

           /**
            * 錯(cuò)誤碼
            */
           protected String errorCode;
           /**
            * 錯(cuò)誤信息
            */
           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);
           }

          }

          自定義全局異常處理類(lèi)

          最后我們?cè)趤?lái)編寫(xiě)一個(gè)自定義全局異常處理的類(lèi)。代碼如下:

          @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);
              }
          }

          因?yàn)檫@里我們只是用于做全局異常處理的功能實(shí)現(xiàn)以及測(cè)試,所以這里我們只需在添加一個(gè)實(shí)體類(lèi)和一個(gè)控制層類(lèi)即可。

          實(shí)體類(lèi)

          又是萬(wàn)能的用戶(hù)表 (^▽^)

          代碼如下:

          public class User implements Serializable{
           private static final long serialVersionUID = 1L;
           /** 編號(hào) */
            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 控制層

          控制層這邊也比較簡(jiǎn)單,使用Restful風(fēng)格實(shí)現(xiàn)的CRUD功能,不同的是這里我故意弄出了一些異常,好讓這些異常被捕獲到然后處理。這些異常中,有自定義的異常拋出,也有空指針的異常拋出,當(dāng)然也有不可預(yù)知的異常拋出(這里我用類(lèi)型轉(zhuǎn)換異常代替),那么我們?cè)谕瓿纱a編寫(xiě)之后,看看這些異常是否能夠被捕獲處理成功吧!

          代碼如下:

          @RestController
          @RequestMapping(value = "/api")
          public class UserRestController {

           @PostMapping("/user")
              public boolean insert(@RequestBody User user) {
               System.out.println("開(kāi)始新增...");
               //如果姓名為空就手動(dòng)拋出一個(gè)自定義的異常!
                  if(user.getName()==null){
                      throw  new BizException("-1","用戶(hù)姓名不能為空!");
                  }
                  return true;
              }

              @PutMapping("/user")
              public boolean update(@RequestBody User user) {
               System.out.println("開(kāi)始更新...");
                 //這里故意造成一個(gè)空指針的異常,并且不進(jìn)行處理
                  String str=null;
                  str.equals("111");
                  return true;
              }

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

              @GetMapping("/user")
              public List<User> findByUser(User user) {
               System.out.println("開(kāi)始查詢(xún)...");
                  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項(xiàng)目基本一樣。

          代碼如下:

          @SpringBootApplication
          public class App 
          {
              public static void main( String[] args )
              {
            SpringApplication.run(App.class, args);
            System.out.println("程序正在運(yùn)行...");
              }
          }

          功能測(cè)試

          我們成功啟動(dòng)該程序之后,使用Postman工具來(lái)進(jìn)行接口測(cè)試。

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

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

          返回參數(shù)為:

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

          示例圖:

          可以看到程序正常返回,并沒(méi)有因自定義的全局異常而影響。

          然后我們?cè)賮?lái)測(cè)試下自定義的異常是否能夠被正確的捕獲并處理。

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

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

          Body參數(shù)為:

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

          返回參數(shù)為:

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

          示例圖:

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

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

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

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

          Body參數(shù)為:

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

          返回參數(shù)為:

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

          示例圖:

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

          那么我們?cè)趤?lái)試試未指定其異常的處理,看該異常是否能夠被捕獲。

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

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

          Body參數(shù)為:

          {"id":1}

          返回參數(shù)為:

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

          這里可以看到它使用了我們?cè)谧远x全局異常處理類(lèi)中的Exception異常處理的方法。到這里,測(cè)試就結(jié)束了。順便再說(shuō)一下,自義定全局異常處理除了可以處理上述的數(shù)據(jù)格式之外,也可以處理頁(yè)面的跳轉(zhuǎn),只需在新增的異常方法的返回處理上填寫(xiě)該跳轉(zhuǎn)的路徑并不使用ResponseBody 注解即可。細(xì)心的同學(xué)也許發(fā)現(xiàn)了在GlobalExceptionHandler類(lèi)中使用的是ControllerAdvice注解,而非RestControllerAdvice注解,如果是用的RestControllerAdvice注解,它會(huì)將數(shù)據(jù)自動(dòng)轉(zhuǎn)換成JSON格式,這種于ControllerRestController類(lèi)似,所以我們?cè)谑褂萌之惓L幚淼闹罂梢赃M(jìn)行靈活的選擇處理。

          其它

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

          項(xiàng)目地址

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

          1、最牛逼的 Java 日志框架,性能無(wú)敵,橫掃所有對(duì)手!
          2、把Redis當(dāng)作隊(duì)列來(lái)用,真的合適嗎?
          3、驚呆了,Spring Boot居然這么耗內(nèi)存!你知道嗎?
          4、牛逼哄哄的 BitMap,到底牛逼在哪?
          5、全網(wǎng)最全 Java 日志框架適配方案!還有誰(shuí)不會(huì)?
          6、30個(gè)IDEA插件總有一款適合你
          7、Spring中毒太深,離開(kāi)Spring我居然連最基本的接口都不會(huì)寫(xiě)了

          點(diǎn)分享

          點(diǎn)收藏

          點(diǎn)點(diǎn)贊

          點(diǎn)在看

          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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毛一级a看免费视频 | 天天综合永久入口 | 国产mv和日韩 | 欧美日屁 |