每次项目搭建,新项目都要重复配置各种依赖,看着那些复杂的框架配置就头疼,今天我要分享一个让无数程序员拍手叫好的方法。
用最简单的方式,搭建出一个企业级的后端项目框架。就算你是刚入门的小白,跟着这个教程,5分钟就能拥有一个完整可用的项目!
开始之前,先准备这些神器
想要顺利开车,得先有个好车对吧?我们的"车"就是这些基础环境:
必备装备清单:
- JDK 17或更高版本(推荐21版本)
-
**MySQL数据库**\](8.x版本最香,5.7也能凑合用)
3步创建你的第一个项目
第1步:IDEA里的魔法操作
打开IDEA,就像打开一个魔法工具箱:
- 选择Spring Boot模板(这就是我们的"项目生成器")
- 选择Maven(想象成项目的"管家",帮你管理所有依赖)
- 选择JDK 21
项目创建示意图

第2步:选择你的"超能力"
Spring Boot就像一个超市,你可以选择需要的"超能力":
- Spring Web:让你的项目能接收网络请求(必选!)
- MySQL :连接数据库的桥梁(必选!)
- Lombok:让代码变得超级简洁(强烈推荐!)
- Spring Boot DevTools:开发时的贴心助手(可选)
依赖选择界面

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

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

可选神器:AOP切面编程
如果你想要更高级的功能,比如记录日志、权限控制等,可以加上这个:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
你甚至可以在启动类上加个注解(但其实不加也行,Spring Boot已经默认开启了):
less
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
企业级项目的"标准配置"
接下来是重头戏!这些代码在任何企业级项目中都会用到,可以说是"一次编写,终身受用"的那种。
项目结构长这样:
scss
└── src
└── main
├── java
│ └── com
│ └── it666
│ └── demo
│ ├── config (配置类)
│ ├── controller (接口层)
│ ├── exception (异常处理)
│ ├── common (通用类)
│ └── utils (工具类)
└── resources
└── application.yml
异常处理:让错误变得"优雅"
在企业级项目中,错误处理非常重要。我们要让错误变得"可预测"、"可控制"。
第1步:定义错误码
就像HTTP状态码一样,我们也要有自己的错误码体系:
arduino
/**
* 自定义错误码枚举
*
* @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内置的异常,我们要有自己的"专属异常":
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步:简化抛异常的代码
写个工具类,让抛异常变得超级简单:
typescript
/**
* 抛异常工具类
*
* @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));
}
}
以后检查参数就超级简单了:
ini
// 如果用户ID为空,就抛出参数错误异常
ThrowUtils.throwIf(userId == null, ErrorCode.PARAMS_ERROR, "用户ID不能为空");
响应格式:让前端开发者爱上你的接口
统一的响应格式就像统一的"包装盒",前端开发者一看就知道怎么处理:
kotlin
/**
* 通用响应类
*
* @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对象太麻烦了,我们再写个工具类:
typescript
/**
* 返回结果工具类
*
* @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);
}
}
现在返回数据就变得超级简单:
kotlin
return ResultUtils.success(userList); // 成功
return ResultUtils.error(ErrorCode.PARAMS_ERROR); // 失败
全局异常处理:再也不怕程序崩溃
最怕的就是程序突然崩溃,用户看到一堆乱七八糟的错误信息。我们要做一个"安全网":
less
/**
* 全局异常处理器
*
* @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
注解,不然会有兼容性问题。
通用请求类:减少重复代码
有些请求参数在很多地方都会用到,比如分页、删除操作,我们可以封装成通用类:
分页请求:
ini
/**
* 分页请求
*
* @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";
}
删除请求:
kotlin
/**
* 删除请求
*
* @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;
}
跨域配置:解决前后端分离的头疼问题
前端和后端分开部署时,经常会遇到跨域问题。简单说就是浏览器不让前端直接访问后端接口。
比如:
这就是跨域!
解决方法很简单,加个全局配置:
typescript
/**
* 全局跨域配置
*
* @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("*");
}
}
大功告成!来验证一下成果
最后,我们修改一下测试接口,让它使用我们的统一响应格式:
less
/**
* 健康检查控制器
*
* @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...