每次项目搭建,新项目都要重复配置各种依赖,看着那些复杂的框架配置就头疼,今天我要分享一个让无数程序员拍手叫好的方法。
用最简单的方式,搭建出一个企业级的后端项目框架。就算你是刚入门的小白,跟着这个教程,5分钟就能拥有一个完整可用的项目!
想要顺利开车,得先有个好车对吧?我们的"车"就是这些基础环境:
必备装备清单:
这些基础安装我就不唠叨了,网上教程多得是。关键是接下来的操作。
打开IDEA,就像打开一个魔法工具箱:

Spring Boot就像一个超市,你可以选择需要的"超能力":

点击创建,Maven就开始为你下载各种"超能力包"了。这时候可以去泡杯茶,等待这个过程完成。
项目创建好后,最重要的事情就是——启动它!

看到控制台输出"Started"字样,恭喜你!你的项目已经成功运行了。这时候可以提交代码到Git,给自己一个小小的成就感。
小贴士: 如果Lombok报错,别慌!手动指定版本就好了:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<optional>true</optional>
</dependency>接下来,把配置文件从application.properties改成application.yml(YAML格式更直观),然后添加这些配置:
spring:
application:
name: demo
server:
port: 8123
servlet:
context-path: /api这就相当于给你的项目起了个名字,指定了它的"门牌号"(端口8123)和"地址前缀"(/api)。
基础项目有了,但还需要一些"高级配件"让它更强大!
想象一下,如果有一个万能工具箱,里面有各种常用工具:日期处理、字符串操作、文件处理、加密解密...这就是Hutool!
比如格式化日期,原来要写一大堆代码,现在一行搞定:DateUtil.formatDate(new Date())
安装方法:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.38</version>
</dependency>你写的接口别人怎么知道怎么用?Knife4j就像一个自动说明书生成器,不仅能生成漂亮的文档,还能直接在页面上测试接口!
重要提醒: 我们用的是Spring Boot 3.x,一定要选OpenAPI 3版本!
安装步骤:
1)添加依赖:
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>2)在配置文件中告诉它扫描哪个包:
# springdoc-openapi
springdoc:
group-configs:
- group: 'default'
packages-to-scan: com.it666.demo.controller
# knife4j
knife4j:
enable: true
setting:
language: zh_cn3)写个测试接口:
@Tag(name = "健康检查", description = "系统健康状态检查接口")
@RestController
@RequestMapping("/health")
public class HealthController {
@Operation(summary = "健康检查", description = "检查系统是否正常运行")
@GetMapping("/")
public String healthCheck() {
return "ok";
}
}重启项目,访问 http://localhost:8123/api/doc.html ,你会看到一个超级漂亮的接口文档页面!

如果你想要更高级的功能,比如记录日志、权限控制等,可以加上这个:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>你甚至可以在启动类上加个注解(但其实不加也行,Spring Boot已经默认开启了):
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}接下来是重头戏!这些代码在任何企业级项目中都会用到,可以说是"一次编写,终身受用"的那种。
项目结构长这样:
└── src
└── main
├── java
│ └── com
│ └── it666
│ └── demo
│ ├── config (配置类)
│ ├── controller (接口层)
│ ├── exception (异常处理)
│ ├── common (通用类)
│ └── utils (工具类)
└── resources
└── application.yml在企业级项目中,错误处理非常重要。我们要让错误变得"可预测"、"可控制"。
第1步:定义错误码
就像HTTP状态码一样,我们也要有自己的错误码体系:
/**
* 自定义错误码枚举
*
* @author NEO
*/
@Getter
public enum ErrorCode {
SUCCESS(0, "ok"),
PARAMS_ERROR(40000, "请求参数错误"),
NOT_LOGIN_ERROR(40100, "未登录"),
NO_AUTH_ERROR(40101, "无权限"),
NOT_FOUND_ERROR(40400, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
SYSTEM_ERROR(50000, "系统内部异常"),
OPERATION_ERROR(50001, "操作失败");
/**
* 状态码
*/
private final int code;
/**
* 错误信息
*/
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
}小技巧: 错误码最好和HTTP状态码保持一致,比如40100对应401未授权,这样更容易记住!
第2步:自定义业务异常
不要直接抛Java内置的异常,我们要有自己的"专属异常":
/**
* 自定义业务异常类
*
* @author NEO
*/
@Getter
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
}
public BusinessException(ErrorCode errorCode, String message) {
super(message);
this.code = errorCode.getCode();
}
}第3步:简化抛异常的代码
写个工具类,让抛异常变得超级简单:
/**
* 抛异常工具类
*
* @author NEO
*/
public class ThrowUtils {
private ThrowUtils() {
// 工具类不允许实例化
}
/**
* 如果条件成立就抛异常
*
* @param condition 条件
* @param runtimeException 异常
*/
public static void throwIf(boolean condition, RuntimeException runtimeException) {
if (condition) {
throw runtimeException;
}
}
/**
* 如果条件成立就抛异常(使用错误码)
*
* @param condition 条件
* @param errorCode 错误码
*/
public static void throwIf(boolean condition, ErrorCode errorCode) {
throwIf(condition, new BusinessException(errorCode));
}
/**
* 如果条件成立就抛异常(自定义错误信息)
*
* @param condition 条件
* @param errorCode 错误码
* @param message 错误信息
*/
public static void throwIf(boolean condition, ErrorCode errorCode, String message) {
throwIf(condition, new BusinessException(errorCode, message));
}
}以后检查参数就超级简单了:
// 如果用户ID为空,就抛出参数错误异常
ThrowUtils.throwIf(userId == null, ErrorCode.PARAMS_ERROR, "用户ID不能为空");统一的响应格式就像统一的"包装盒",前端开发者一看就知道怎么处理:
/**
* 通用响应类
*
* @param <T> 数据类型
* @author NEO
*/
@Data
public class BaseResponse<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 状态码
*/
private int code;
/**
* 数据
*/
private T data;
/**
* 提示信息
*/
private String message;
public BaseResponse(int code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public BaseResponse(int code, T data) {
this(code, data, "");
}
public BaseResponse(ErrorCode errorCode) {
this(errorCode.getCode(), null, errorCode.getMessage());
}
}但每次都要手动创建ResponseBase对象太麻烦了,我们再写个工具类:
/**
* 返回结果工具类
*
* @author NEO
*/
public class ResultUtils {
private ResultUtils() {
// 工具类不允许实例化
}
/**
* 成功响应
*
* @param data 数据
* @param <T> 数据类型
* @return 响应结果
*/
public static <T> BaseResponse<T> success(T data) {
return new BaseResponse<>(0, data, "ok");
}
/**
* 失败响应
*
* @param errorCode 错误码
* @return 响应结果
*/
public static BaseResponse<Void> error(ErrorCode errorCode) {
return new BaseResponse<>(errorCode);
}
/**
* 失败响应(自定义错误码和信息)
*
* @param code 错误码
* @param message 错误信息
* @return 响应结果
*/
public static BaseResponse<Void> error(int code, String message) {
return new BaseResponse<>(code, null, message);
}
/**
* 失败响应(使用错误码枚举和自定义信息)
*
* @param errorCode 错误码
* @param message 错误信息
* @return 响应结果
*/
public static BaseResponse<Void> error(ErrorCode errorCode, String message) {
return new BaseResponse<>(errorCode.getCode(), null, message);
}
}现在返回数据就变得超级简单:
return ResultUtils.success(userList); // 成功
return ResultUtils.error(ErrorCode.PARAMS_ERROR); // 失败最怕的就是程序突然崩溃,用户看到一堆乱七八糟的错误信息。我们要做一个"安全网":
/**
* 全局异常处理器
*
* @author NEO
*/
@Hidden
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理业务异常
*
* @param e 业务异常
* @return 错误响应
*/
@ExceptionHandler(BusinessException.class)
public BaseResponse<Void> businessExceptionHandler(BusinessException e) {
log.error("BusinessException: {}", e.getMessage(), e);
return ResultUtils.error(e.getCode(), e.getMessage());
}
/**
* 处理运行时异常
*
* @param e 运行时异常
* @return 错误响应
*/
@ExceptionHandler(RuntimeException.class)
public BaseResponse<Void> runtimeExceptionHandler(RuntimeException e) {
log.error("RuntimeException: {}", e.getMessage(), e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
}
}重要提醒: 因为我们用的是Spring Boot 3.4+和OpenAPI 3的Knife4j,必须加上@Hidden注解,不然会有兼容性问题。
有些请求参数在很多地方都会用到,比如分页、删除操作,我们可以封装成通用类:
分页请求:
/**
* 分页请求
*
* @author NEO
*/
@Data
@Schema(description = "分页请求参数")
public class PageRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 当前页号
*/
@Schema(description = "当前页号", example = "1")
private int pageNum = 1;
/**
* 页面大小
*/
@Schema(description = "页面大小", example = "10")
private int pageSize = 10;
/**
* 排序字段
*/
@Schema(description = "排序字段", example = "createTime")
private String sortField;
/**
* 排序顺序(默认降序)
*/
@Schema(description = "排序顺序", example = "descend")
private String sortOrder = "descend";
}删除请求:
/**
* 删除请求
*
* @author NEO
*/
@Data
@Schema(description = "删除请求参数")
public class DeleteRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 要删除数据的ID
*/
@Schema(description = "要删除的数据ID", required = true, example = "1")
private Long id;
}前端和后端分开部署时,经常会遇到跨域问题。简单说就是浏览器不让前端直接访问后端接口。
比如:
这就是跨域!
解决方法很简单,加个全局配置:
/**
* 全局跨域配置
*
* @author NEO
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 对所有接口生效
registry.addMapping("/**")
// 允许发送Cookie
.allowCredentials(true)
// 允许所有域名访问(生产环境建议指定具体域名)
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("*");
}
}最后,我们修改一下测试接口,让它使用我们的统一响应格式:
/**
* 健康检查控制器
*
* @author NEO
*/
@Tag(name = "健康检查", description = "系统健康状态检查接口")
@RestController
@RequestMapping("/health")
public class HealthController {
@Operation(summary = "健康检查", description = "检查系统是否正常运行")
@GetMapping("/")
public BaseResponse<String> healthCheck() {
return ResultUtils.success("ok");
}
}重启项目,访问接口文档,你会发现响应格式变得非常规范!

到这里,你已经拥有了一个企业级后端项目的完整框架!这个框架包含了:
这套框架的最大优势是什么?
现在你可以在这个基础上愉快地开发各种功能了!需要数据库操作?加上MyBatis-Plus。需要Redis缓存?加上Spring Data Redis。需要消息队列?加上RabbitMQ...
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。