1. 三層架構核心職責
層級 | 職責說明 | 關鍵技術 / 注解 |
---|---|---|
Controller(控制器) | 1. 接收前端請求(HTTP) 2. 封裝參數、校驗 3. 調用 Service 處理業務 4. 返回視圖 / 數據給前端 | @Controller 、@GetMapping 等 |
Service(業務層) | 1. 封裝復雜業務邏輯 2. 協調 Mapper 完成數據庫操作 3. 事務管理、權限校驗等 | @Service 、@Transactional |
Dao(Data Access Object)(數據訪問(持久)層) | 1. 直接操作數據庫(增刪改查)(SQL 執行) 2. 封裝 CRUD 操作 3. 結果映射為 Java 對象 | @Mapper 、XML 映射或注解(@Select ) |
基于 MVC 三層架構,流程說明:
- 前端:發起 HTTP 請求(如瀏覽器訪問
/list
)。 - Controller 層:接收請求,調用 Service 處理,最終返回 JSON 響應。
- Service 層:封裝業務邏輯(解析文件、數據處理),依賴 Dao 獲取原始數據。
- Dao 層:專注數據訪問(本例讀
user.txt
,未來可擴展數據庫操作)。 - 資源層:存放文件或數據庫,提供原始數據。
原代碼:
package com.itxiaoli.controller;import cn.hutool.core.io.IoUtil;
import com.itxiaoli.pojo.User;
import lombok.Data;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;/*** 用戶信息controller*/
@RestController
public class UserController {@RequestMapping("/list")public List<User> list() throws FileNotFoundException {//1.加載并讀取user.txt文件來獲取用戶數據//InputStream in= new FileInputStream("絕對路徑"); 不推薦,打包之后絕對目錄會找不到InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());//2.解析用戶信息,封裝成用戶對西昂 ->list集合List<User> userList = lines.stream().map(line -> {String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id, username, password, name, age, updateTime);}).toList();//3.將list集合中的數據返回為(json)格式return userList;}
}
2.分層解耦優勢詳解
1. 職責分離(單一職責原則)
原代碼:
// UserController承擔多重職責(文件讀取、數據解析、HTTP處理)
public List<User> list() throws FileNotFoundException {InputStream in = ...; // DAO職責List<String> lines = ...; // 文件操作User user = parseUser(lines);// Service職責return user; // Controller職責
}
優化后:
// Controller層 - 僅負責請求分發
@RestController
public class UserController {@Autowired private UserService userService;@RequestMapping("/list")public List<User> list() {return userService.findAllUser(); // 單一職責}
}
2. 高內聚低耦合
-
Service 依賴抽象而非實現:
@Service public class UserServiceImpl implements UserService {@Autowired private UserDao userDao; // 依賴接口而非實現類 }
-
DAO 層可無縫切換實現
(如從文件到數據庫):
// 原文件實現 public class FileUserDaoImpl implements UserDao {...}// 新增數據庫實現 public class JdbcUserDaoImpl implements UserDao {...}
3. 可擴展性增強
-
開閉原則實踐:
// 新增緩存功能,無需修改原有代碼 @Service public class CachedUserServiceImpl implements UserService {@Autowired private UserDao userDao;@Autowired private CacheManager cacheManager;@Overridepublic List<User> findAllUser() {// 優先從緩存獲取List<User> users = cacheManager.get("users");if (users == null) {users = userDao.findAll();cacheManager.put("users", users);}return users;} }
4. 可測試性提升
-
Controller 單元測試示例:
@SpringBootTest public class UserControllerTest {@Autowired private MockMvc mockMvc;@MockBean private UserService userService;@Testpublic void testListUsers() throws Exception {// Mock Service返回值when(userService.findAllUser()).thenReturn(List.of(new User()));// 驗證Controller響應mockMvc.perform(get("/list")).andExpect(status().isOk()).andExpect(jsonPath("$.size()").value(1));} }
5. 代碼復用性
-
DAO 層復用示例:
// UserService和AdminService均可復用UserDao @Service public class AdminService {@Autowired private UserDao userDao;public User lockUser(Long userId) {User user = userDao.findById(userId);// 業務邏輯...return userDao.update(user);} }
3.分層解耦后代碼結構
1. Controller 層
@RestController
public class UserController {@Autowired private UserService userService;@RequestMapping("/list")public List<User> list() {return userService.findAllUser();}
}
2. Service 層
// 接口定義
public interface UserService {List<User> findAllUser();
}// 實現類
@Service
public class UserServiceImpl implements UserService {@Autowired private UserDao userDao;@Overridepublic List<User> findAllUser() {List<String> lines = userDao.findAll();return lines.stream().map(this::parseUser).collect(Collectors.toList());}private User parseUser(String line) {// 數據解析邏輯}
}
3. DAO 層
// 接口定義
public interface UserDao {List<String> findAll();
}// 文件實現
@Repository
public class FileUserDaoImpl implements UserDao {@Overridepublic List<String> findAll() {InputStream in = getClass().getResourceAsStream("/user.txt");return IoUtil.readLines(in, StandardCharsets.UTF_8);}
}
4. 分層解耦實踐建議
1. 依賴注入最佳實踐
-
使用構造器注入替代字段注入:
@Service public class UserServiceImpl implements UserService {private final UserDao userDao;@Autowired // 可省略,Spring 4.3+支持public UserServiceImpl(UserDao userDao) {this.userDao = userDao;} }
2. 事務管理
-
在 Service 層聲明式管理事務:
@Service public class UserServiceImpl implements UserService {@Transactional(rollbackFor = Exception.class)public void transferPoints(Long fromId, Long toId, int points) {// 業務邏輯} }
3. 異常處理
-
在 Controller 層統一處理異常:
@RestControllerAdvice public class GlobalExceptionHandler {@ExceptionHandler(FileNotFoundException.class)public ResponseEntity<String> handleFileNotFound(Exception e) {return ResponseEntity.status(404).body("文件未找到");} }
5.分層解耦的挑戰與應對
挑戰 | 解決方案 |
---|---|
代碼量增加 | 使用 Lombok 減少樣板代碼,合理使用接口默認方法簡化實現 |
性能開銷 | 層間調用開銷可忽略,避免過度分層(如簡單項目可合并 Service 和 DAO) |
過度設計 | 根據項目規模選擇架構(如小型項目可采用兩層架構),優先滿足當前需求 |
調試復雜度 | 使用 AOP 記錄層間調用日志,結合 IDE 調用鏈分析工具 |
6. 分層架構演進路徑
- 簡單項目(初期):
- Controller → Service(與 DAO 合并)
- 中等規模項目:
- Controller → Service → DAO
- 大型復雜項目:
- Controller → Facade → Service → Manager → DAO
- 增加領域模型層(Domain Model)
- 引入事件總線(Event Bus)解耦模塊