Spring Boot三层架构最佳实践:分层代码写法是否正确?
现有写法不符合三层架构最佳实践
你当前的代码主要存在3个核心问题:
- 职责越界严重:
ResponseEntity、HttpStatus是Spring Web层专属的传输层对象,Service作为业务逻辑层,不应该感知任何和HTTP协议、Web交互相关的逻辑。如果在Service里直接返回Web层对象,后续你要复用Service逻辑做定时任务、RPC接口、离线任务的时候,会强依赖Spring Web包,根本无法独立复用。 - 异常处理完全冗余失效:两层都写了大而全的
try-catch捕获通用Exception,但Service层已经把所有异常都捕获、封装成ResponseEntity返回了,根本不会有异常抛到Controller层,你写在Controller里的catch块是永远不会触发的死代码。另外这种吞异常的写法会丢失异常栈信息,后续排查数据库连接失败、SQL错误等问题的时候根本找不到根因。 - 逻辑耦合错位:判断列表为空返回204状态码、组装HTTP响应体这类属于请求处理的逻辑,本来就是Controller层的职责,硬塞到Service层只会让业务层逻辑变混乱。另外你Controller里的请求处理方法用了
private修饰,这是Spring MVC的写法错误,请求映射方法必须是public,否则框架无法正常代理调用。
各层职责边界与正确写法
三层架构的核心是每层只做自己职责内的事,不越界、不冗余,各层的明确分工如下:
- Repository层:只做纯数据访问,封装所有数据库操作,直接返回领域实体/集合,不做业务判断、不捕获异常,数据库抛出的异常直接向上抛出即可。
- Service层:只处理纯业务逻辑,包括业务规则校验、流程编排、事务控制、调用Repository获取/存储数据,遇到业务错误直接抛自定义业务异常即可,返回值就是纯业务对象(比如
List<Customer>、单个Customer实体),绝对不要引入Web层相关的类。
Service层参考写法:// 注意返回值是纯业务集合,不涉及任何Web相关对象 public List<Customer> getAllCustomers() { List<Customer> customers = new ArrayList<>(); customerRepository.findAll().forEach(customers::add); // 这里只保留业务相关逻辑,比如数据权限过滤、业务规则校验 // 如果业务规则定义"客户列表为空属于业务异常",直接抛自定义业务异常,不要在这里拼HTTP响应 return customers; } - Controller层:只负责接收请求参数、做基础参数校验、调用Service拿到业务结果、组装HTTP响应(设置对应状态码、返回结构)。不要在每个接口方法里单独写try-catch,推荐用Spring提供的
@RestControllerAdvice做全局异常处理,统一拦截所有层抛出的异常,封装成统一格式的错误响应,避免重复代码。
Controller层参考写法:
全局异常处理器参考写法:@GetMapping("/viewList") public ResponseEntity<List<Customer>> getAllCustomers() { List<Customer> customers = customerService.getAllCustomers(); if (customers.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return ResponseEntity.ok(customers); }@RestControllerAdvice public class GlobalExceptionHandler { // 捕获所有未处理的服务端异常 @ExceptionHandler(Exception.class) public ResponseEntity<String> handleServerException(Exception e) { // 生产环境这里要先打印错误日志留痕,再返回对用户友好的提示 return new ResponseEntity<>("服务内部异常,请稍后重试", HttpStatus.INTERNAL_SERVER_ERROR); } // 自定义业务异常可以单独写对应处理逻辑,返回对应状态码 @ExceptionHandler(CustomerNotFoundException.class) public ResponseEntity<String> handleCustomerNotFound(CustomerNotFoundException e) { return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); } }
常见误区说明
- 不需要把Service层的所有额外逻辑都删掉:Service层要保留的是和业务强相关的处理逻辑,比如数据权限过滤、事务控制、业务规则校验、多步骤业务流程编排这类和Web传输无关的逻辑,只需要把HTTP状态码设置、响应结构封装这类Web专属逻辑移到Controller层即可。
- 不要每层都做异常捕获:异常应该在最适合处理的层统一收口,不要在中间层随意吞异常。数据层异常、业务异常都可以直接向上抛,最终到全局异常处理器里统一封装响应,既减少重复代码,也能保留完整异常栈方便排查问题。
内容的提问来源于stack exchange,提问作者renad sahal
相关产品推荐
相关产品推荐

