【Java项目-企悦抽】05-搭建项目+自定义状态码/自定义异常+统一返回结果
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🎯 你正在阅读「Java项目-企悦抽」系列文章 🎯
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🔥 弹简特 个人主页
❄️ 个人专栏直通车:
✨ 靠热爱去书写自己,靠勇敢去书写生活!
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🌟 博主简介:

文章目录:
一、前言
兄弟们,我们前面完成了对于该项目的文档性内容,那么有了文档的参考,从本期开始,我们就可以正式的进入项目实战环节了,本期将会带你搭建环境、自定义状态码+自定义异常,最后封装统一返回结果
二、项目搭建
1、创建工程

pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.6</version>
<relativePath/>
</parent>
<groupId>com.zhongge</groupId>
<artifactId>lottery-system</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>lottery-system</name>
<description>lottery-system</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- RabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- redis 服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring web依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok依赖包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、分层

3、创建对应的层

三、状态码和异常
1、自定义状态码
为什么咱们要自定义一些异常状态码呢?
-
第1点它的明确性,我们有这些以数字为基础的错误码,它就可以明确的表示当前的一个错误状态,可以更好的避免我们一些问题的所在,总比我们返回一堆错误描述好多。
-
第2点就是对客户端友好,我们作为调用方,我们呢,是希望人家给我们返回的是可以针对这些错误码去进行一些判断。
-
第3点是维护性,我们这样定一些错误码的话,我们可以更好的去维护系统。
-
第4点是错误分类。就是对于我们的系统来说,我们是对系统进行分层的,那么我们可以对每一层进行一个错误的分层处理,那这样的话哪一层错误,我们就可以直接就处理到了。
-
第5点就是容易检索,因为你容易检索,我们定义这样的错误码,它的明确性很高,我们也可以容易检索是什么问题,因为你每一层有啥错误就很容易检索。
那么这些错误码类我们写在哪一层?我们肯定是写在这个common层,因为呢这个错误码它可以是控制层的错误码,也可以是服务层的错误码,也可以是持久层的错误码,所以我们写在那个common通用业务层这样就可以兼顾控制层、服务层、持久层。

那我们这个错误码类里面应该有哪些信息呢?
首先是有一个错误码,是整型,然后再有一个描述(String类型的描述)。就是一个错误码对应的一个错误描述。
代码:
@Data
public class ErrorCode {
/**
* 错误码
*/
private final Integer code;
/**
* 错误描述
*/
private final String message;
//构造方法
public ErrorCode(Integer code, String message) {
this.code = code;
this.message = message;
}
}
那定义完错误码之后,我们接下来就需要给我们当前的系统去进行一个错误的分类,比如说我们对分层进行分类:我们有哪些层呢?
有控制层、服务层和持久层,原则上我们要对这三层进行错误的定义。
不过实际上:我们对持久层dao不定义错误码
为什么呢?因为我们的持久层它可能是sql报错,它有许多的错误码,我们定义不过来。因此我们要在service层把dao层的错误给它包住,故这一点可体现我们是必须要在服务层里面去定义这个错误码的。
好,有了以上条件,我们就可以定一个controller和一个service控制层和服务层的两层的错误码。那除此之外,我们还要定义一个全局的错误码,就是它不包含控制层,也不包含服务层,就是其他未知错误,我们可以把它定义成个全局的错误码。
首先我们定义控制层的错误码类

public interface ControllerErrorCodeConstants {
//TODO 具体有哪一些错误码 我们写到对应代码的时候再说
//------人员模块的错误码--------
//------活动模块的错误码--------
//------奖品模块的错误码--------
//------抽奖的错误码--------
}
其次是定义服务层的错误码类

public interface ServiceErrorCodeConstants {
//TODO 具体有哪一些错误码 我们写到对应代码的时候再说
//------人员模块的错误码--------
//------活动模块的错误码--------
//------奖品模块的错误码--------
//------抽奖的错误码--------
}
最后定义未知错误码类

public interface GlobalErrorCodeConstants {
// 成功的错误码
ErrorCode SUCCESS = new ErrorCode(200, "成功! ");
//系统异常:500
ErrorCode INTERNAL_SYSTEM_ERROR = new ErrorCode(500, "系统异常! ");
//未知异常
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误! ");
}
2、自定义异常类
我们定义异常类的策略是根据分层来定义的。比如我们的看controller层去定一个统一的异常类,对它进行抓取,我们的service层我们去另外统一的搞一个异常类,对service进行抓取。
所以我们开始吧:
先创建一个包exception

控制层的异常抓取类。

@Data
@EqualsAndHashCode(callSuper = true)
public class ControllerException extends RuntimeException{
/**
* 异常码
* @see com.zhongge.lotterysystem.common.errorcode.ControllerErrorCodeConstants //参考
*/
private Integer code;
/**
* 异常信息
*/
private String message;
//无参构造 是为了 序列化的
public ControllerException() {
}
public ControllerException(Integer code, String message) {
this.code = code;
this.message = message;
}
public ControllerException(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
}
服务层异常抓取类

@Data
@EqualsAndHashCode(callSuper = true)
public class ServiceException extends RuntimeException{
/**
* 异常码
* @see com.zhongge.lotterysystem.common.errorcode.ServiceErrorCodeConstants //参考
*/
private Integer code;
/**
* 异常信息
*/
private String message;
//无参构造 是为了 序列化的
public ServiceException() {
}
public ServiceException(Integer code, String message) {
this.code = code;
this.message = message;
}
public ServiceException(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
}
注意:

OK,到此我们的异常类就结束了
四、统一返回结果
1、为什么封装统一返回结果
我们接收参数的那一层,也就是我们的控制层,同时也是和我们前端打交道那层,我们要控制层去封装一个统一的返回结果。
这里我们是使用一个泛型,使用泛型的话,它是允许我们返回任何一个类型。
为什么我们要统一封装一个http请求接口调用的返回结果呢?
比如有一个testA接口,返回的是AResult类型。还有一个testB接口访问的是BResult类的型,这两个接口本身是没有问题的,a返回a的,b的b的,互不影响,但是它对前端的处理有问题。

什么问题呢?
由于我们一般前端拿到结果之后先判断本次调用是否成功。
那是如何判断呢?
一般前端拿到一个结果之后,他会先判断每次调用是否成功,对于testA()接口来说,他会拿到aResult,简写为a, 然后拿到a里面的状态码code来代表本次调用的结果,如果是code是200就代表成功,如果不是200,就表示不成功。

那对于testB()来说,如果返回值bResult里面封装的不是code的字段,而是b.errorcode,那对于前端来说他要判断a的code。也要判断b的errorcode这样会比较麻烦,此时对于前端来说,如果他们有一个统一的行为来管理后端的结构,到底是成功还是失败,所以对于前端来说,他肯定是希望我们后端无论调什么接口,都给他一个统一的反馈结果。比如说一个统一的叫做commonResult.code。然后使用commonResult.date来获取统一的数据,这样的话,我们的前端就会方便操作很多,因此我们才会设置泛型,让我们后端任意返回我们的数据类型。

说明白之后,我们来写一下这个统一的返回类型怎么写?
@Data
//因为我们是进行http进行传输的,因此我们可能需要进行一些序列化。所以我们暂时让它实现一下Serializable。
public class commonResult<T> implements Serializable {
//code表示调用是否成功 返回的错误码
private Integer code;
//数据 正常返回的数据
private T data;
//消息 错误码描述
private String message;
}
返回成功我们希望封装哪些数据呢?

返回失败我们希望封装那些数据呢?

所以我们上述的代码还需要加两个静态方法。
其实返回统计结果它是有那个格式规则,也就是公式,就是属性加两个静态方法,一个成功一个失败,一般是这两种。
@Data
//因为我们是进行http进行传输的,因此我们可能需要进行一些序列化。所以我们暂时让它实现一下Serializable。
public class commonResult<T> implements Serializable {
//code表示调用是否成功 返回的错误码
private Integer code;
//数据 正常返回的数据
private T data;
//消息 错误码描述
private String message;
//静态方法返回成功的时候
public static <T> commonResult<T> success(T data){
commonResult<T> result = new commonResult<>();
result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
result.data = data;
result.message = "";//或者 GlobalErrorCodeConstants.SUCCESS.getMessage()
return result;
}
//静态方法返回失败的时候
public static <T> commonResult<T> error(Integer code, String message){
//断言一下 如果传递进来的code是200 即 success 那么就没必要往下走了(这一步是为了做充分的判断)
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code),
"code 不是错误的异常");
//查询走到这里 说明是错误的code
commonResult<T> result = new commonResult<>();
result.code = code;
//result.data = null;//由于是错误的 所以我们就没有必要往这个code里面去塞数据了,默认就是null
result.message = message;
return result;
}
//静态方法返回失败的时候:通过我们之前返回的ErrorCode
public static <T> commonResult<T> error(ErrorCode errorCode){
return error(errorCode.getCode(),errorCode.getMessage());
}
}
2、代码解释
2.1 属性

2.2 成功的静态方法

2.3 失败的静态方法
第一种写法

第二种写法
第二种写法 就似乎直接传递我们的状态码对象 然后调用一下第一种写法的方法即可

2.4 对断言代码的解释
Assert.isTrue(
!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code),
"code 不是错误的状态码"
);
1). 这行代码的作用
一句话总结:
强制检查:你传入的 code 绝对不能是成功码(比如 200)。
如果传了成功码,程序直接报错停止,避免逻辑出错。
2). 拆解每一部分
① Assert.isTrue(条件, 错误信息)
这是 Spring 框架提供的断言工具。
作用:
- 如果 条件为 true → 什么都不做,继续往下走
- 如果 条件为 false → 直接抛出异常,并显示后面的错误信息
简单说:
我断言这个条件一定成立,不成立就报错!
② !GlobalErrorCodeConstants.SUCCESS.getCode().equals(code)
拆成两部分:
GlobalErrorCodeConstants.SUCCESS.getCode()
→ 拿到成功状态码,比如 200!→ 取反(不是)
整句意思:
传入的 code 不是成功码(200)
③ 合起来的逻辑
Assert.isTrue(
传入的code不是成功码,
"code 不是错误的状态码"
);
翻译成人话:
我要求:你调用 error() 方法时,必须传错误码。
如果你不小心传了 200(成功码),我就直接报错提醒你!
3). 为什么要写这行?
因为你的方法叫 error()
它的功能是:创建一个“失败响应”
逻辑上:
- 失败响应 → 状态码绝对不能是 200
- 200 是成功,不是失败
如果有人不小心写:
commonResult.error(200, "操作成功");
这就逻辑矛盾了!
这行断言就是为了:
提前拦截这种低级错误,避免bug藏在代码里。
4). 运行效果举例
正确调用(断言通过)
commonResult.error(500, "服务器异常");
500 ≠ 200 → 条件成立 → 正常执行
错误调用(断言报错)
commonResult.error(200, "成功了");
200 == 200 → 条件不成立 → 直接抛异常
提示信息:
code 不是错误的状态码
5). 总结
Assert.isTrue(你必须满足的条件, 不满足时报错的消息);
你这行的意思:
调用错误方法时,状态码不能是成功码,否则报错!
朋友们,我们本期就到这里吧,下一期我们会完成序列化、日志以及加密等等工作,如果有用的老铁点赞👍关注,或者评论区逛一逛,您的支持是我创作的最大动力,咱们下一期见啦~~
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)