Spring Boot项目从MVC架构改造为分层架构的API设计咨询
Spring Boot分层架构改造与层间隔离API实现示例
一、核心分层隔离思路
- 控制器层(Controller):仅负责请求接收、响应封装,通过**DTO(数据传输对象)**与服务层交互,不直接操作实体类或数据访问层
- 服务层(Service):定义业务接口明确层间交互API,实现类处理核心业务逻辑,负责DTO与实体类的转换,依赖数据访问层接口而非实现
- 数据访问层(DAO/Repository):仅处理数据持久化操作,依赖实体类,对外暴露数据操作接口
- 层间隔离核心:用DTO阻断实体类在各层的直接传递,避免业务逻辑与数据结构耦合,同时控制对外暴露的数据范围
二、改造后代码示例
1. 定义DTO(数据传输对象)
创建与业务场景匹配的DTO,仅包含需要在层间传递的字段,屏蔽实体类的内部细节:
// UserDTO.java public class UserDTO { private String username; private String password; // 仅保留业务需要的字段,避免暴露实体的全量属性 // getter、setter、构造方法省略 } // AnnonceDTO.java public class AnnonceDTO { private String title; private String content; // 不包含id、date这类由服务层统一处理的字段 // getter、setter、构造方法省略 }
2. 控制器层改造
控制器仅处理请求参数接收与响应返回,所有业务逻辑委托给服务层:
// UserController.java @Controller public class UserController { private final UserService userService; // 构造注入,避免字段注入 public UserController(UserService userService) { this.userService = userService; } @PostMapping("/saveUser") public String saveUser(@ModelAttribute("user") UserDTO userDTO) { // 控制器不处理业务逻辑(如密码加密),仅传递DTO到服务层 userService.saveUser(userDTO); return "redirect:/users"; } } // AnnonceRestController.java @RestController @RequestMapping("/api") public class AnnonceRestController { private final AnnonceService annonceService; public AnnonceRestController(AnnonceService annonceService) { this.annonceService = annonceService; } @GetMapping("/announce-all") public List<AnnonceDTO> findAll() { // 返回DTO而非实体,隔离内部数据结构 return annonceService.findAllAnnonces(); } @PostMapping("/announce-create") public AnnonceDTO addAnnonce(@RequestBody AnnonceDTO annonceDTO) { // 服务层处理id、日期等业务规则,控制器不干预 return annonceService.saveAnnonce(annonceDTO); } }
3. 服务层接口与实现
定义服务接口明确层间交互规范,实现类处理业务逻辑并完成DTO与实体的转换:
// UserService.java(服务接口,定义层间交互API) public interface UserService { void saveUser(UserDTO userDTO); // 其他业务方法接口 } // UserServiceImpl.java(服务实现类) @Service public class UserServiceImpl implements UserService { private final UserDao userDao; private final PasswordEncoder passwordEncoder; public UserServiceImpl(UserDao userDao, PasswordEncoder passwordEncoder) { this.userDao = userDao; this.passwordEncoder = passwordEncoder; } @Override @Transactional public void saveUser(UserDTO userDTO) { // 业务逻辑处理:密码加密、DTO转实体 User user = new User(); user.setUsername(userDTO.getUsername()); user.setPassword(passwordEncoder.encode(userDTO.getPassword())); // 其他字段赋值 userDao.saveUser(user); } } // AnnonceService.java(服务接口) public interface AnnonceService { List<AnnonceDTO> findAllAnnonces(); AnnonceDTO saveAnnonce(AnnonceDTO annonceDTO); } // AnnonceServiceImpl.java(服务实现类) @Service public class AnnonceServiceImpl implements AnnonceService { private final AnnonceDao annonceDao; public AnnonceServiceImpl(AnnonceDao annonceDao) { this.annonceDao = annonceDao; } @Override public List<AnnonceDTO> findAllAnnonces() { // 实体转DTO,屏蔽内部数据细节 return annonceDao.findAll().stream() .map(this::convertToDTO) .collect(Collectors.toList()); } @Override @Transactional public AnnonceDTO saveAnnonce(AnnonceDTO annonceDTO) { Annonce annonce = new Annonce(); annonce.setTitle(annonceDTO.getTitle()); annonce.setContent(annonceDTO.getContent()); annonce.setId(0); // 业务规则统一处理 annonce.setDate(LocalDateTime.now()); // 服务层负责日期赋值 Annonce savedAnnonce = annonceDao.saveAnnonce(annonce); return convertToDTO(savedAnnonce); } // 私有方法:实体转DTO private AnnonceDTO convertToDTO(Annonce annonce) { AnnonceDTO dto = new AnnonceDTO(); dto.setTitle(annonce.getTitle()); dto.setContent(annonce.getContent()); return dto; } }
4. 数据访问层接口
DAO层仅定义数据持久化操作,不涉及任何业务逻辑:
// UserDao.java @Repository public interface UserDao { void saveUser(User user); // 其他数据操作方法 } // AnnonceDao.java @Repository public interface AnnonceDao { List<Annonce> findAll(); Annonce saveAnnonce(Annonce annonce); // 其他数据操作方法 }
三、分层隔离关键要点
- 依赖倒置:上层依赖下层的接口而非实现,比如控制器依赖
UserService接口、服务层依赖UserDao接口,后续替换实现类不影响上层代码 - DTO隔离:用DTO在控制器与服务层之间传递数据,避免实体类的内部结构暴露到外部,同时防止层间直接耦合实体
- 单一职责:控制器仅处理请求响应、服务层处理业务逻辑、DAO层仅处理数据操作,各层职责边界清晰
内容的提问来源于stack exchange,提问作者readJohn
相关产品推荐
相关产品推荐

