Spring Boot 在企业级项目中的真正价值,从来不是"自动装配"或者"快速启动"。这些只是工具层面的便利性,而项目能否真正落地,决定因素永远是:
-
后端能否提供稳定、统一、可扩展的接口给前端调用
-
业务逻辑是否清晰、可维护、便于扩展
-
数据库访问是否规范、可靠、性能可控
-
项目结构是否合理,后期迭代是否轻松
换言之,一个优秀的 Spring Boot 项目,应该是一套围绕"业务目标"而构建的工程体系。让我们一起看看一个企业级项目中,接口层、业务层、数据库层到底如何协作,如何组织代码,如何使整个项目保持多年可维护性。
目录
[1.1 为什么分层结构如此重要?](#1.1 为什么分层结构如此重要?)
[1.2 分层的真实作用(逐层解析)](#1.2 分层的真实作用(逐层解析))
[(3)Repository / Mapper:负责"数据访问"](#(3)Repository / Mapper:负责“数据访问”)
[(4)DTO / Entity / VO 的作用](#(4)DTO / Entity / VO 的作用)
[2.1 REST 控制器的原理与写法](#2.1 REST 控制器的原理与写法)
[为什么要使用 @RequestMapping("/api/user")?](#为什么要使用 @RequestMapping("/api/user")?)
[为什么使用 @RequestBody?](#为什么使用 @RequestBody?)
[2.2 参数接收方式:HTTP 的三种常见模式](#2.2 参数接收方式:HTTP 的三种常见模式)
[(1)@RequestParam:用于接收 URL 参数(GET 常用)](#(1)@RequestParam:用于接收 URL 参数(GET 常用))
[(2)@RequestBody:接收前端发送的 JSON(POST 常用)](#(2)@RequestBody:接收前端发送的 JSON(POST 常用))
[2.3 为什么需要统一返回结构?](#2.3 为什么需要统一返回结构?)
[3.1 为什么不能把业务写在 Controller?](#3.1 为什么不能把业务写在 Controller?)
[3.2 标准 Service 写法](#3.2 标准 Service 写法)
[3.3 参数校验的重要性](#3.3 参数校验的重要性)
[四、数据库访问层:Repository 与 Mapper 的实战写法(增强版)](#四、数据库访问层:Repository 与 Mapper 的实战写法(增强版))
[4.1 JPA:适合 CRUD 项目的轻量方案](#4.1 JPA:适合 CRUD 项目的轻量方案)
[4.2 MyBatis:适用于复杂 SQL、业务量大的场景](#4.2 MyBatis:适用于复杂 SQL、业务量大的场景)
[Mapper 接口](#Mapper 接口)
一、项目整体结构
实际项目开发中,最重要的不是"能跑",而是可维护、可扩展、可协作 。
分层架构之所以能在行业里流传几十年,就是因为它完美符合企业级项目的需求。
1.1 为什么分层结构如此重要?
一个真实项目往往会迭代数年,业务逻辑复杂度会逐渐升高,团队成员可能不断更换。
如果后端不分层,会出现以下严重问题:
-
代码难以阅读 → 新人接手困难
-
业务逻辑、数据库操作混在一起 → 难以测试
-
任何修改都可能导致不可控的连锁反应
-
Controller 臃肿,Mapper 过度膨胀,Service 沦为拼接代码的中转站
因此合理的分层是后端工程质量的核心基础。
1.2 分层的真实作用(逐层解析)
(1)Controller:负责"请求与响应"
Controller 的职责非常单一:
-
接收前端参数
-
对参数进行初步校验
-
调用 Service
-
将结果转换为统一格式返回前端
注意:Controller 不允许写任何业务逻辑。
(2)Service:负责"业务逻辑"
业务逻辑包括:
-
参数进一步校验
-
查询数据库以判断业务条件
-
组合多个数据源的结果
-
执行业务流程(例如下单 → 扣库存 → 生成支付单)
-
调用多个 Mapper/Repository
-
实现事务逻辑
Service 层也不负责数据库操作,而是使用 Repository/Mapper 作为唯一入口。
(3)Repository / Mapper:负责"数据访问"
Repository(JPA)或 Mapper(MyBatis)是真正负责与数据库交互的部分。
这层的特点是:
-
不写业务逻辑
-
不做流程控制
-
专注 SQL 或 ORM 映射
Repository/Mapper 是底层,它屏蔽了数据库细节,Service 不需要关注 SQL 语句本身。
(4)DTO / Entity / VO 的作用
前后端传输的数据实际上经历三个阶段:
| 类型 | 作用 | 来源/去向 |
|---|---|---|
| DTO | Controller 接收前端参数 | 来自前端 |
| Entity | 表结构映射 | 用于数据库存储 |
| VO | 返回前端展示的数据 | 给前端 |
这三个对象的分工明确,可以有效避免前端参数直接影响数据库结构。
二、接口是如何提供给前端调用的
在现代 Web 开发中,前后端通常通过 REST API 通信。Spring Boot 默认支持基于 HTTP + JSON 的接口风格,因此只需编写简单的注解即可完成路由映射。
2.1 REST 控制器的原理与写法
一个 Controller 本质上是:
-
Spring Boot 启动时自动扫描的 Bean
-
每个方法都能映射到一个 HTTP 路由
-
负责处理 JSON 输入、返回 JSON 输出
@RestController
@RequestMapping("/api/user")
public class UserController {@Autowired private UserService userService; @PostMapping("/create") public Result<UserVO> createUser(@RequestBody UserDTO dto) { return Result.success(userService.createUser(dto)); }}
为什么要使用 @RequestMapping("/api/user")?
-
便于管理同一业务域的接口
-
避免路由冲突
-
更符合 REST 的语义化设计
为什么使用 @RequestBody?
-
接收 JSON 格式参数
-
可映射为 DTO 类
-
便于字段校验
2.2 参数接收方式:HTTP 的三种常见模式
Spring Boot 完整支持 Web API 中的所有参数类型。
(1)@RequestParam:用于接收 URL 参数(GET 常用)
GET /api/user/detail?id=1
接收
@GetMapping("/detail")
public Result<UserVO> detail(@RequestParam Long id) {
return Result.success(userService.detail(id));
}
(2)@RequestBody:接收前端发送的 JSON(POST 常用)
{
"name": "Alice",
"email": "alice@example.com"
}
接收
@PostMapping("/update")
public Result<Boolean> update(@RequestBody UserDTO dto) {
return Result.success(userService.update(dto));
}
(3)@PathVariable:接收路径变量
GET /api/user/11
接收
@GetMapping("/{id}")
public Result<UserVO> getUser(@PathVariable Long id) {
return Result.success(userService.detail(id));
}
2.3 为什么需要统一返回结构?
在企业开发中,不可能让每个 Controller 自己决定响应格式,这会导致前端无法稳定解析数据。
统一响应结构的好处包括:
-
前端解析更简单,只需判断
code == 0 -
后端接口风格一致
-
异常情况统一处理
-
接口文档更加规范
通用结构如下:
{
"code": 0,
"msg": "success",
"data": { ... }
}
使用 Result 包装类即可实现。
三、业务逻辑层(Service):项目的核心大脑
Service 层是整个项目的业务中心,它负责执行流程、编排逻辑、做条件判断、验证参数、决定是否允许数据写入数据库。
3.1 为什么不能把业务写在 Controller?
虽然 Controller 写业务"看起来更快",但会导致:
-
Controller 越写越大,无法维护
-
无法复用逻辑
-
无法进行单元测试
-
无法进行事务控制(Service 才允许)
-
系统结构失去扩展性
因此,企业级项目中统一要求:
Controller 只能做"输入输出",其余全部在 Service。
3.2 标准 Service 写法
业务接口:
public interface UserService {
UserVO createUser(UserDTO dto);
UserVO detail(Long id);
Boolean update(UserDTO dto);
}
业务实现:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public UserVO createUser(UserDTO dto) {
// 1. 校验邮箱是否重复
if (userRepository.findByEmail(dto.getEmail()).isPresent()) {
throw new BusinessException("邮箱已存在,无法重复注册");
}
// 2. 创建实体对象
UserEntity entity = new UserEntity();
entity.setName(dto.getName());
entity.setEmail(dto.getEmail());
entity.setCreateTime(LocalDateTime.now());
// 3. 写入数据库
userRepository.save(entity);
// 4. 返回 VO
return new UserVO(entity.getId(), entity.getName(), entity.getEmail());
}
}
这段代码体现了企业开发中常见的业务处理方式:
-
校验参数
-
判断状态(例如是否重复、是否满足业务条件)
-
进行业务逻辑组合
-
操作数据库
-
组装返回结果
3.3 参数校验的重要性
后端需要保证数据的可靠性,例如:
public class UserDTO {
@NotBlank(message = "姓名不能为空")
private String name;
@Email(message = "邮箱格式不正确")
private String email;
}
在 Controller 中使用:
public Result<UserVO> createUser(@Valid @RequestBody UserDTO dto)
这样读者只需关注业务逻辑,参数合法性交给 Spring 校验。
四、数据库访问层:Repository 与 Mapper 的实战写法(增强版)
Spring Boot 默认支持两种主流方式:
JPA(快速自动 SQL)
MyBatis(精细控制 SQL)
项目中常按业务复杂度选择。
4.1 JPA:适合 CRUD 项目的轻量方案
JPA 的优点:
-
无需写 SQL
-
使用实体对象操作数据库
-
支持分页、排序、动态查询
-
更符合"面向对象"的编程方式
实体类映射说明
@Entity
@Table(name = "t_user")
@Data
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private LocalDateTime createTime;
}
说明:
-
@Entity:映射成数据库表 -
@Table(name = "t_user"):指定表名 -
@Id:主键字段 -
@GeneratedValue:自增主键策略
4.2 MyBatis:适用于复杂 SQL、业务量大的场景
MyBatis 的优点:
-
SQL 完全可控
-
性能更可优化
-
复杂查询使用 XML 更灵活
-
大型项目、大数据场景首选
Mapper 接口
@Mapper
public interface UserMapper {
UserEntity selectByEmail(String email);
}
XML 定义 SQL
<select id="selectByEmail" resultType="UserEntity">
SELECT * FROM t_user WHERE email = #{email}
</select>
MyBatis 方式更接近传统关系型数据库使用方式,可精准控制执行效率与查询结构。
五、从接口到数据库
从前端请求到数据库写入的完整路径:

前端发送 JSON 请求
↓
Controller 接收 DTO、校验、调用 Service
↓
Service 执行业务判断、校验逻辑、组织流程
↓
Repository / Mapper 发起数据库操作
↓
数据库写入或查询
↓
Service 组装 VO
↓
Controller 返回 Result<T>
↓
前端收到统一格式的响应