在構建 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多平台發佈