分层解耦(Controller,Service,Dao)
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)解耦模块