參考:https://www.cnblogs.com/xuwujing/p/10933082.html
1、首先,需要引入maven依賴包,如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 <parent> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-parent</artifactId> 9 <version>2.3.12.RELEASE</version> 10 <relativePath /> <!-- lookup parent from repository --> 11 </parent> 12 <groupId>com.bie</groupId> 13 <artifactId>SpringbootException</artifactId> 14 <version>0.0.1-SNAPSHOT</version> 15 <name>SpringbootException</name> 16 <description>Demo project for Spring Boot</description> 17 18 <properties> 19 <java.version>1.8</java.version> 20 </properties> 21 22 <dependencies> 23 <!-- 引入springboot web模塊 --> 24 <dependency> 25 <groupId>org.springframework.boot</groupId> 26 <artifactId>spring-boot-starter-web</artifactId> 27 </dependency> 28 <!-- 引入fastjson的依賴 --> 29 <dependency> 30 <groupId>com.alibaba</groupId> 31 <artifactId>fastjson</artifactId> 32 <version>1.2.41</version> 33 </dependency> 34 35 <dependency> 36 <groupId>org.springframework.boot</groupId> 37 <artifactId>spring-boot-starter-test</artifactId> 38 <scope>test</scope> 39 <exclusions> 40 <exclusion> 41 <groupId>org.junit.vintage</groupId> 42 <artifactId>junit-vintage-engine</artifactId> 43 </exclusion> 44 </exclusions> 45 </dependency> 46 </dependencies> 47 48 <build> 49 <plugins> 50 <plugin> 51 <groupId>org.springframework.boot</groupId> 52 <artifactId>spring-boot-maven-plugin</artifactId> 53 </plugin> 54 </plugins> 55 </build> 56 57 </project>
SpringBoot中有一個ControllerAdvice的注解,使用該注解表示開啟了全局異常的捕獲,我們只需再自定義一個方法,然后使用ExceptionHandler注解,在該注解的value屬性里面,定義捕獲異常的類型,即可對這些捕獲的異常進行統一的處理。
2、自定義基礎接口類。
首先定義一個基礎的接口類,自定義的錯誤描述枚舉類需實現該接口。
1 package com.bie.enums; 2 3 public interface BaseErrorInfoInterface { 4 5 // 錯誤碼 6 public String getResultCode(); 7 8 // 錯誤描述 9 public String getResultMsg(); 10 11 }
3、自定義枚舉類。
然后我們這里在自定義一個枚舉類,並實現該接口。而使用枚舉類的好處是處理異常的時候,可以通過枚舉類直接獲取到錯誤碼、錯誤描述,方便調用。
1 package com.bie.enums; 2 3 public enum CommonEnum implements BaseErrorInfoInterface { 4 5 // 數據操作錯誤定義 6 SUCCESS("200", "接口調用成功!"), 7 8 BODY_NOT_MATCH("400", "請求的數據格式不符!"), 9 10 SIGNATURE_NOT_MATCH("401", "請求的數字簽名不匹配!"), 11 12 NOT_FOUND("404", "未找到該資源!"), 13 14 INTERNAL_SERVER_ERROR("500", "服務器內部錯誤!"), 15 16 SERVER_BUSY("503", "服務器正忙,請稍后再試!"); 17 18 // 錯誤碼 19 private String resultCode; 20 21 // 錯誤描述 22 private String resultMsg; 23 24 CommonEnum(String resultCode, String resultMsg) { 25 this.resultCode = resultCode; 26 this.resultMsg = resultMsg; 27 } 28 29 @Override 30 public String getResultCode() { 31 return resultCode; 32 } 33 34 @Override 35 public String getResultMsg() { 36 return resultMsg; 37 } 38 39 }
4、自定義異常類。
然后我們在來自定義一個異常類,用於處理我們發生的業務異常。
1 package com.bie.exception; 2 3 import com.bie.enums.BaseErrorInfoInterface; 4 5 public class BizException extends RuntimeException { 6 7 /** 8 * 9 */ 10 private static final long serialVersionUID = -6329783845738305585L; 11 12 // 錯誤碼 13 protected String errorCode; 14 // 錯誤信息 15 protected String errorMsg; 16 17 public BizException() { 18 super(); 19 } 20 21 public BizException(BaseErrorInfoInterface errorInfoInterface) { 22 super(errorInfoInterface.getResultCode()); 23 this.errorCode = errorInfoInterface.getResultCode(); 24 this.errorMsg = errorInfoInterface.getResultMsg(); 25 } 26 27 public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) { 28 super(errorInfoInterface.getResultCode(), cause); 29 this.errorCode = errorInfoInterface.getResultCode(); 30 this.errorMsg = errorInfoInterface.getResultMsg(); 31 } 32 33 public BizException(String errorMsg) { 34 super(errorMsg); 35 this.errorMsg = errorMsg; 36 } 37 38 public BizException(String errorCode, String errorMsg) { 39 super(errorCode); 40 this.errorCode = errorCode; 41 this.errorMsg = errorMsg; 42 } 43 44 public BizException(String errorCode, String errorMsg, Throwable cause) { 45 super(errorCode, cause); 46 this.errorCode = errorCode; 47 this.errorMsg = errorMsg; 48 } 49 50 public String getErrorCode() { 51 return errorCode; 52 } 53 54 public void setErrorCode(String errorCode) { 55 this.errorCode = errorCode; 56 } 57 58 public String getErrorMsg() { 59 return errorMsg; 60 } 61 62 public void setErrorMsg(String errorMsg) { 63 this.errorMsg = errorMsg; 64 } 65 66 public String getMessage() { 67 return errorMsg; 68 } 69 70 @Override 71 public Throwable fillInStackTrace() { 72 return this; 73 } 74 75 }
5、自定義數據格式。
順便這里我們定義一下數據的傳輸格式,作用主要用於返回給前端的數據格式。
1 package com.bie.utils; 2 3 import com.alibaba.fastjson.JSONObject; 4 import com.bie.enums.BaseErrorInfoInterface; 5 import com.bie.enums.CommonEnum; 6 7 public class ResultBody { 8 9 // 響應代碼 10 private String code; 11 12 // 響應消息 13 private String message; 14 15 // 響應結果 16 private Object result; 17 18 public ResultBody() { 19 } 20 21 public ResultBody(BaseErrorInfoInterface errorInfo) { 22 this.code = errorInfo.getResultCode(); 23 this.message = errorInfo.getResultMsg(); 24 } 25 26 public String getCode() { 27 return code; 28 } 29 30 public void setCode(String code) { 31 this.code = code; 32 } 33 34 public String getMessage() { 35 return message; 36 } 37 38 public void setMessage(String message) { 39 this.message = message; 40 } 41 42 public Object getResult() { 43 return result; 44 } 45 46 public void setResult(Object result) { 47 this.result = result; 48 } 49 50 /** 51 * 成功 52 * 53 * @return 54 */ 55 public static ResultBody success() { 56 return success(null); 57 } 58 59 /** 60 * 成功 61 * 62 * @param data 63 * @return 64 */ 65 public static ResultBody success(Object data) { 66 ResultBody rb = new ResultBody(); 67 rb.setCode(CommonEnum.SUCCESS.getResultCode()); 68 rb.setMessage(CommonEnum.SUCCESS.getResultMsg()); 69 rb.setResult(data); 70 return rb; 71 } 72 73 /** 74 * 失敗 75 */ 76 public static ResultBody error(BaseErrorInfoInterface errorInfo) { 77 ResultBody rb = new ResultBody(); 78 rb.setCode(errorInfo.getResultCode()); 79 rb.setMessage(errorInfo.getResultMsg()); 80 rb.setResult(null); 81 return rb; 82 } 83 84 /** 85 * 失敗 86 */ 87 public static ResultBody error(String code, String message) { 88 ResultBody rb = new ResultBody(); 89 rb.setCode(code); 90 rb.setMessage(message); 91 rb.setResult(null); 92 return rb; 93 } 94 95 /** 96 * 失敗 97 */ 98 public static ResultBody error(String message) { 99 ResultBody rb = new ResultBody(); 100 rb.setCode("-1"); 101 rb.setMessage(message); 102 rb.setResult(null); 103 return rb; 104 } 105 106 @Override 107 public String toString() { 108 return JSONObject.toJSONString(this); 109 } 110 111 }
6、自定義全局異常處理類。
最后我們再來編寫一個自定義全局異常處理的類,可以用於處理各類異常。
1 package com.bie.exception; 2 3 import javax.servlet.http.HttpServletRequest; 4 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.springframework.web.bind.annotation.ControllerAdvice; 8 import org.springframework.web.bind.annotation.ExceptionHandler; 9 import org.springframework.web.bind.annotation.ResponseBody; 10 11 import com.bie.enums.CommonEnum; 12 import com.bie.utils.ResultBody; 13 14 @ControllerAdvice 15 public class GlobalExceptionHandler { 16 17 private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); 18 19 /** 20 * 處理自定義的業務異常 21 * 22 * @param req 23 * @param e 24 * @return 25 */ 26 @ExceptionHandler(value = BizException.class) 27 @ResponseBody 28 public ResultBody bizExceptionHandler(HttpServletRequest req, BizException e) { 29 logger.error("發生業務異常!原因是:{}", e.getErrorMsg()); 30 return ResultBody.error(e.getErrorCode(), e.getErrorMsg()); 31 } 32 33 /** 34 * 處理空指針的異常 35 * 36 * @param req 37 * @param e 38 * @return 39 */ 40 @ExceptionHandler(value = NullPointerException.class) 41 @ResponseBody 42 public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e) { 43 logger.error("發生空指針異常!原因是:", e); 44 return ResultBody.error(CommonEnum.BODY_NOT_MATCH); 45 } 46 47 /** 48 * 處理其他異常 49 * 50 * @param req 51 * @param e 52 * @return 53 */ 54 @ExceptionHandler(value = Exception.class) 55 @ResponseBody 56 public ResultBody exceptionHandler(HttpServletRequest req, Exception e) { 57 logger.error("未知異常!原因是:", e); 58 return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR); 59 } 60 61 }
7、創建一個用戶的實體類,如下所示:
1 package com.bie.po; 2 3 import java.io.Serializable; 4 5 import com.alibaba.fastjson.JSONObject; 6 7 public class User implements Serializable { 8 9 /** 10 * 11 */ 12 private static final long serialVersionUID = 1360679426784375558L; 13 14 // 編號 15 private int id; 16 // 姓名 17 private String name; 18 // 年齡 19 private int age; 20 21 public User() { 22 } 23 24 public int getId() { 25 return id; 26 } 27 28 public void setId(int id) { 29 this.id = id; 30 } 31 32 public String getName() { 33 return name; 34 } 35 36 public void setName(String name) { 37 this.name = name; 38 } 39 40 public int getAge() { 41 return age; 42 } 43 44 public void setAge(int age) { 45 this.age = age; 46 } 47 48 public String toString() { 49 return JSONObject.toJSONString(this); 50 } 51 52 }
8、Controller 控制層。
控制層這邊也比較簡單,使用Restful風格實現的CRUD功能,主要是Restful風格的,根據請求方式get、post、put、delete,而請求路徑是一個,主要根據請求方式來做區分操作。
1 package com.bie.controller; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import org.springframework.web.bind.annotation.DeleteMapping; 7 import org.springframework.web.bind.annotation.GetMapping; 8 import org.springframework.web.bind.annotation.PostMapping; 9 import org.springframework.web.bind.annotation.PutMapping; 10 import org.springframework.web.bind.annotation.RequestBody; 11 import org.springframework.web.bind.annotation.RequestMapping; 12 import org.springframework.web.bind.annotation.RestController; 13 14 import com.bie.exception.BizException; 15 import com.bie.po.User; 16 17 @RestController 18 @RequestMapping(value = "/api") 19 public class UserRestController { 20 21 @PostMapping("/user") 22 public boolean insert(@RequestBody User user) { 23 System.out.println("開始新增..."); 24 // 如果姓名為空就手動拋出一個自定義的異常! 25 if (user.getName() == null) { 26 throw new BizException("-1", "用戶姓名不能為空!"); 27 } 28 return true; 29 } 30 31 @PutMapping("/user") 32 public boolean update(@RequestBody User user) { 33 System.out.println("開始更新..."); 34 // 這里故意造成一個空指針的異常,並且不進行處理 35 String str = null; 36 str.equals("111"); 37 return true; 38 } 39 40 @DeleteMapping("/user") 41 public boolean delete(@RequestBody User user) { 42 System.out.println("開始刪除..."); 43 // 這里故意造成一個異常,並且不進行處理 44 Integer.parseInt("abc123"); 45 return true; 46 } 47 48 @GetMapping("/user") 49 public List<User> findByUser(User user) { 50 System.out.println("開始查詢..."); 51 List<User> userList = new ArrayList<>(); 52 User user2 = new User(); 53 user2.setId(1); 54 user2.setName("xuwujing"); 55 user2.setAge(18); 56 userList.add(user2); 57 return userList; 58 } 59 60 }
9、接口功能測試,使用postman進行測試,如下所示:
9.1、首先進行查詢,查看程序是否正常運行,使用GET 方式進行請求,如下所示:
9.2、進行插入操作,使用POST方式進行請求,如下所示:
9.3、進行修改操作,使用PUT方式進行請求,如下所示:
9.4、進行刪除操作,使用DELETE方式進行請求,如下所示:
10、整體思路解析,按照步驟操作,按道理來說,這個思路是很優秀的,那么下面來分析一下這個設計思路。
10.1、在自己的方法中拋出自定義異常,而拋出的自定義異常是被全局異常類進行捕獲處理的。
對拋出的自定義異常,在全局異常處理類中進行處理,然后返回的信息,是封裝到自定義數據格式類中的,這樣返回給前端的數據格式,就可以根據自己的需求進行設計。
10.2、而對於全局異常類中,可以定義捕獲其他類型的異常。而在捕獲其他異常之后,返回的數據封裝到自定義數據格式里面,而對於其他異常而已直接使用定義的枚舉類中來選擇異常內容。
雖然,這種設計不能說100%完美,但是設計的已經很優秀了,基本可以滿足日常需求,贊一個。