什么是 Spring MVC 三层架构?

#Java#面试八股

什么是 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 的维度差别,逻辑就自洽了。

笔记加载中…