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 方式进行请求,如下所示:

SpringBoot2.3.12.RELEASE优雅的全局异常处理(模板一)_spring

9.2、进行插入操作,使用POST方式进行请求,如下所示:

SpringBoot2.3.12.RELEASE优雅的全局异常处理(模板一)_maven_02

9.3、进行修改操作,使用PUT方式进行请求,如下所示: 

SpringBoot2.3.12.RELEASE优雅的全局异常处理(模板一)_数据格式_03

9.4、进行删除操作,使用DELETE方式进行请求,如下所示:
 SpringBoot2.3.12.RELEASE优雅的全局异常处理(模板一)_maven_04

 

10、整体思路解析,按照步骤操作,按道理来说,这个思路是很优秀的,那么下面来分析一下这个设计思路。

10.1、在自己的方法中抛出自定义异常,而抛出的自定义异常是被全局异常类进行捕获处理的。

SpringBoot2.3.12.RELEASE优雅的全局异常处理(模板一)_spring_05

  对抛出的自定义异常,在全局异常处理类中进行处理,然后返回的信息,是封装到自定义数据格式类中的,这样返回给前端的数据格式,就可以根据自己的需求进行设计。

SpringBoot2.3.12.RELEASE优雅的全局异常处理(模板一)_spring_06

10.2、而对于全局异常类中,可以定义捕获其他类型的异常。而在捕获其他异常之后,返回的数据封装到自定义数据格式里面,而对于其他异常而已直接使用定义的枚举类中来选择异常内容。

SpringBoot2.3.12.RELEASE优雅的全局异常处理(模板一)_maven_07

虽然,这种设计不能说100%完美,但是设计的已经很优秀了,基本可以满足日常需求,赞一个。