什么是 Spring MVC 三层架构?
结论先行:三层架构是按职责纵向切分:表现层(Controller,接请求、校验、组装响应)、业务层(Service,事务边界与业务规则)、持久层(DAO/Mapper,只做数据存取)。MVC 是表现层内部的模式(Model-View-Controller),与三层不是同一维度:三层管"代码分层",MVC 管"请求如何在表现层流转"。分层的目标只有一个——让依赖单向、职责单一、每层可独立测试与替换。
一、分层与依赖方向
浏览器 / App
│ HTTP
┌───▼─────────────────────────────────────────┐
│ 表现层 Controller + 参数校验 + 统一响应 │ ← MVC 中的 C
│ DTO / VO 转换 │
└───┬─────────────────────────────────────────┘
│ 调用(只向下依赖)
┌───▼─────────────────────────────────────────┐
│ 业务层 Service:事务、领域规则、编排 │
└───┬─────────────────────────────────────────┘
│
┌───▼─────────────────────────────────────────┐
│ 持久层 Mapper/Repository:SQL、ORM 映射 │
└───┬─────────────────────────────────────────┘
│ JDBC
数据库 / 缓存 / 外部服务
二、每层职责与禁区
| 层 | 核心职责 | 常用注解/组件 | 不该做的事 |
|---|---|---|---|
| 表现层 | 参数绑定、校验、鉴权、响应包装、异常转换 | @RestController、@Valid、@ControllerAdvice | 写业务规则、直接拼 SQL、开事务 |
| 业务层 | 业务规则、事务边界、多表编排、领域校验 | @Service、@Transactional | 处理 HttpServletRequest、返回 ResponseEntity |
| 持久层 | CRUD、条件构造、结果映射 | @Mapper、@Repository、JdbcTemplate | 写业务判断、调用远程服务 |
三、代码:一条链路的三种对象
// 表现层:只做"收参 → 调用 → 包装",不写业务
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public Result<Long> create(@Valid @RequestBody CreateOrderCmd cmd) {
return Result.ok(orderService.create(cmd)); // 业务全在 Service
}
}
// 业务层:事务边界在 Service,而不是 Controller
@Service
public class OrderService {
private final OrderMapper orderMapper;
private final StockClient stockClient;
public OrderService(OrderMapper orderMapper, StockClient stockClient) {
this.orderMapper = orderMapper;
this.stockClient = stockClient;
}
@Transactional(rollbackFor = Exception.class)
public Long create(CreateOrderCmd cmd) {
stockClient.deduct(cmd.getSkuId(), cmd.getCount()); // 远程扣减
Order order = Order.from(cmd); // 领域对象组装
orderMapper.insert(order);
return order.getId();
}
}
// 持久层:只有 SQL 与映射
@Mapper
public interface OrderMapper {
@Insert("insert into t_order(user_id, sku_id, count, status) " +
"values(#{userId}, #{skuId}, #{count}, #{status})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(Order order);
}
四、常见追问
- 问:三层架构和 MVC 是一回事吗? 不是。MVC 是表现层内部的三角色分工(Controller 收请求、Model 装数据、View 渲染),三层是全局纵向分层;SSM 项目里二者叠加使用。
- 问:DTO、VO、DO、PO 有什么区别? DTO 是跨层/跨进程传输对象,VO 是返回给前端的视图对象,DO 是领域对象,PO 是与表结构一一对应的持久化对象。小项目可合并,但对外接口不要直接暴露 PO。
- 问:Service 一定要有接口吗? 不必。接口的价值是"多实现/远程代理",单实现时直接写类更清晰;MyBatis 的 Mapper 是例外(框架需要接口生成代理)。
- 问:Controller 里能不能直接调 Mapper? 技术上能,但绕过事务与业务校验,属于典型分层破坏,Code Review 应拦住。
- 问:分层之后循环依赖怎么办? 按"上层依赖下层"单向设计;出现 Service 互调时抽公共服务或发事件,别用
@Lazy掩盖。
小结
三层架构一句话:Controller 只做协议转换,Service 只做业务与事务,Mapper 只做数据。答题时用"依赖单向 + 职责表格 + 事务边界在 Service"三点支撑,再区分三层与 MVC 的维度差别,逻辑就自洽了。