在构建 Spring Boot 应用时,初学者最容易犯的错误就是把所有的逻辑——参数接收、业务判断、SQL 查询——都塞进一个类里。这种“面条式代码”不仅难以维护,而且充满安全隐患。
为了解决这个问题,现代后端开发普遍采用 三层架构 (Three-Tier Architecture)。这种架构的核心思想是**“各司其职”**。
今天,我们就来拆解 Controller、Service、Repository 这三大核心组件,并深入探讨 DTO 与 Entity 在这场数据之旅中扮演的关键角色。
一、 三大核心角色:各司其职
在一个规范的 Spring Boot 项目中,每个请求的处理就像是在餐厅点菜,三个角色配合默契:
1. 表现层:Controller (服务员)
-
注解:
@RestController - 职责:路由 API,只负责“接待”。
-
工作内容:
- 监听 HTTP 请求(GET/POST)。
- 将前端传来的 JSON 解析为 Java 对象。
- 做最基本的参数校验(比如手机号格式对不对)。
- 关键原则:Controller 绝不触碰数据库,也不写复杂的业务逻辑。它只负责把任务派发给 Service。
- 比喻:餐厅的服务员。他负责把菜单递给客人,记下客人点的菜,然后把单子送到后厨。他不会自己去炒菜。
2. 业务层:Service (厨师)
-
注解:
@Service - 职责:核心业务逻辑。
-
工作内容:
- 它是整个系统的“大脑”。
- 执行业务规则(如:检查余额是否充足、计算折扣、触发报警)。
- 数据转换中心:负责将 DTO 转换为 Entity,或将 Entity 转换为 DTO。
- 调用 Repository 获取数据或保存数据。
- 比喻:后厨的主厨。他拿到服务员的单子,负责切菜、烹饪、调味。如果缺食材了,他会叫库管去拿。
3. 数据层:Repository (库管)
-
注解:
@Repository - 职责:数据库操作 (CRUD)。
-
工作内容:
- 它是系统的“底层”。
- 只负责与数据库对话(执行 Insert, Update, Delete, Select)。
- 返回的是与数据库表一一对应的 Entity 对象。
- 比喻:仓库管理员。他不管这道菜是给谁吃的,也不管好不好吃,他只负责把食材(数据)拿出来,或者把成品存进去。
二、 两个数据载体:Entity 与 DTO 的双人舞
在三层架构中,数据需要在不同的层级间传递。为什么我们需要两种对象来承载数据?
1. Entity (实体对象)
- 定义:数据库表的镜像。
- 特征:类名通常与表名对应,字段与列对应。
- 作用:它是持久化数据的载体。
- 局限:它是隐私的。Entity 可能包含“密码”、“盐值”、“逻辑删除标记”等不应该暴露给前端的字段。
2. DTO (数据传输对象)
- 定义:API 接口的契约。
- 特征:普通的 POJO,没有任何数据库注解。
-
作用:
- 安全过滤:把 Entity 中的敏感字段(如密码)过滤掉。
- 数据聚合:有时一个 API 需要返回两张表的数据(如:用户信息 + 订单概况),DTO 可以把多个 Entity 的数据聚合在一起。
- 解耦:如果数据库表结构变了,只要 DTO 不变,前端就感知不到,保证了接口的稳定性。
三、 数据的奇幻漂流:完整流程图解
让我们通过“创建数据”和“查询数据”两个场景,看看数据是如何在 JSON、DTO 和 Entity 之间流转的。
场景一:创建数据 (Write Flow)
任务:用户注册(前端发送 JSON)。
流程:JSON ➡️ Controller ➡️ Service ➡️ Repository ➡️ DB
-
JSON -> DTO (Controller 层)
- 前端发送 JSON 数据。
- Controller 接收请求,Spring Boot 自动将 JSON 反序列化为
UserCreateDTO。
-
DTO -> Entity (Service 层)
- Controller 将
UserCreateDTO传给 Service。 - Service 进行转换(例如使用 MapStruct),将
UserCreateDTO转化为UserEntity。 - 注意:此时 Service 可能会处理密码加密等逻辑。
- Controller 将
-
Entity -> DB (Repository 层)
- Service 将
UserEntity传给 Repository。 - Repository 执行
save(entity),数据入库。
- Service 将
场景二:查询数据 (Read Flow)
任务:获取用户详情(返回 JSON)。
流程:DB ➡️ Repository ➡️ Service ➡️ Controller ➡️ JSON
-
DB -> Entity (Repository 层)
- Repository 根据 ID 从数据库查询数据。
- 返回完整的
UserEntity(包含密码字段)。
-
Entity -> DTO (Service 层)
- Repository 将
UserEntity传给 Service。 - Service 进行转换,将
UserEntity转化为UserResponseDTO。 - 关键点:在转换过程中,Service 剔除了密码字段,只保留用户名和头像。
- Repository 将
-
DTO -> JSON (Controller 层)
- Service 将安全的
UserResponseDTO返回给 Controller。 - Controller 将其序列化为 JSON,响应给前端。
- Service 将安全的
四、 代码实战:这一层层是怎么调用的?
为了加深理解,我们来看一段伪代码:
// 1. Controller: 负责路由和参数接收
@RestController
public class UserController {
@Autowired
private UserService userService; // 引用 Service
@PostMapping("/users")
public UserResponseDTO createUser(@RequestBody UserCreateDTO dto) {
// 接收 DTO,传给 Service
return userService.register(dto);
}
}
// 2. Service: 负责业务逻辑和转换
@Service
public class UserService {
@Autowired
private UserRepository userRepository; // 引用 Repository
public UserResponseDTO register(UserCreateDTO dto) {
// 逻辑:DTO 转 Entity
UserEntity entity = new UserEntity();
entity.setUsername(dto.getUsername());
// 逻辑:调用 Repository 保存
userRepository.save(entity);
// 逻辑:Entity 转 DTO (准备返回)
UserResponseDTO response = new UserResponseDTO();
response.setId(entity.getId());
response.setUsername(entity.getUsername());
return response;
}
}
// 3. Repository: 负责数据库操作
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
// 继承了基本的 save, findById 等方法
}
五、 总结
Spring Boot 的三层架构就像一个精密运转的工厂:
- Controller 是 大门,负责把原材料(JSON)收进来,变成工单(DTO)。
- Service 是 车间,负责核心加工,它把工单(DTO)变成产品(Entity),或者把产品包装成商品(DTO)。
- Repository 是 仓库,只负责保管产品(Entity)。
通过这种设计,我们实现了**“外层契约(DTO)”与“内层存储(Entity)”**的完美隔离,让代码既安全又易于维护。
本文由mdnice多平台发布