Skip to content

MVC 是最经典、也是很多架构模式的源头——MVVM、MVI 都是它的"后代",Spring MVC 更是 Java 后端的标配。平时写 Spring Boot 天天用,但 MVC 的"三部分职责、数据怎么流转、为什么后来又演化出 MVVM/MVI"一直没系统梳理过。这篇把 MVC 从头理清,作为架构模式系列的第一篇。

一、解决什么问题

想象一个没有分层的小程序:界面渲染、用户交互、数据读写、业务逻辑,全写在一个类/一个文件里。结果是——改一处 UI 可能碰坏数据逻辑,改业务逻辑又担心影响展示,代码越堆越乱

MVC 的核心思想一句话:把"数据"、"展示"、"控制"三个关注点拆开,各管各的

二、三部分职责

部分职责不做什么
Model(模型)数据 + 业务逻辑不碰界面
View(视图)展示数据 + 接收用户输入不碰业务逻辑
Controller(控制器)协调两者:接收输入、调 Model、选 View不碰具体数据

关键在分层:Model 不知道 View 长什么样,View 不知道数据从哪来,Controller 是中间协调者。

三、数据流

MVC 的数据流是一条环:

用户操作 View → Controller 收到指令 → 更新 Model → Model 变化 → View 刷新

以"提交订单"为例:

  1. 用户在页面上点"下单"(View 层)
  2. Controller 收到请求,调 Service 处理(Model 层)
  3. Service 更新订单数据(Model 变化)
  4. 结果返回给页面,页面刷新展示(View 更新)

MVC 数据流:用户操作经 Controller 更新 Model,Model 变化刷新 View

四、Spring MVC 落地

Spring MVC 是 MVC 在 Java Web 的标准实现,三层对应:

java
@Controller                       // Controller 层:接收请求、调 Service、返回视图
public class OrderController {
    @GetMapping("/order/{id}")
    public String detail(@PathVariable Long id, Model model) {
        Order order = orderService.getById(id);   // 调 Service(业务层)拿数据
        model.addAttribute("order", order);
        return "order/detail";                     // 返回视图名
    }
}

@Service                          // 业务层:业务逻辑
public class OrderService {
    public Order getById(Long id) { return orderMapper.selectById(id); }
}
  • Controller(表现层)@Controller / @RestController,接收请求、返回视图或数据
  • View:模板(Thymeleaf)或前后端分离时的 JSON 数据
  • DispatcherServlet:前端控制器,统一接收请求、分发给具体 Controller(这是 Spring MVC 的入口)

注意:MVC 的"Model"是个抽象概念,Spring 落地时把它拆成了更细的三层。对应关系如下:

MVC(抽象概念)Spring 实际落地
Model(数据 + 业务逻辑)拆成三层:@Service(业务逻辑)/ @Mapper(数据访问)/ Entity(数据实体)
View(展示)Thymeleaf 模板 / JSON
Controller(协调)@Controller / @RestController

所以代码里不叫"Model 层",而是具体叫"业务层 / 持久层 / 实体"——@Service 只是 Model 的一部分(业务逻辑那一层),不是整个 Model。

五、MVC 的问题(为什么后来有 MVVM/MVI)

MVC 用了这么多年,但有两个痛点,正是后续模式要解决的:

痛点 1:Controller 和 View 耦合紧

Controller 本该只做"协调",却被迫记住了 View 的一堆细节:

java
// 这个 Controller 里,耦合了 View 的好几处细节:
@GetMapping("/order/{id}")
public String detail(@PathVariable Long id, Model model) {
    Order order = orderService.getById(id);
    model.addAttribute("order", order);   // 耦合点①:塞什么数据、字段名叫 "order"
    return "order/detail";                 // 耦合点②:视图文件名写死
}

前端一改——比如页面重命名成 order/show.html、字段从 order 改成 data——Controller 就要跟着改两处。Controller 和 View 绑在一起,前端动后端就动

痛点 2:View 里逻辑越堆越重

View 本该只做"展示",结果业务逻辑全塞了进来:

jsp
<!-- 反面教材:View(JSP)里塞满了业务逻辑,MVC 的 "V" 名存实亡 -->
<%
    List<Order> orders = orderDao.findAll();      // View 里查数据库
    double total = 0;
    for (Order o : orders) {                       // View 里做业务计算
        if ("PAID".equals(o.getStatus())) {
            total += o.getAmount();
        }
    }
%>
<p>已支付总额:<%= total %></p>                     <!-- View 里既算又展示 -->

查库、循环、判断全在 View 里——Model 和 Controller 的活儿都被 View 抢了,这就是"V 名存实亡"。

所以有了演化:

  • MVP:用 Presenter 替代 Controller,把 View 和业务彻底隔离(见《MVP 模式》)
  • MVVM:用 ViewModel + 双向绑定,View 只声明"绑谁",不用手动更新(见《MVVM 模式》)
  • MVI:单向数据流,状态不可变,彻底消灭"状态散落各处"(见《MVI 模式》)

小结

  • MVC 把"数据/展示/控制"拆成三层,各管各的,是架构分层的鼻祖
  • 数据流:用户操作 → Controller → Model → View 刷新
  • Spring MVC 落地:Controller(@Controller)+ Model(@Service/@Repository)+ View(模板)+ DispatcherServlet 入口
  • MVC 的痛点(Controller 和 View 耦合、View 过重)催生了 MVP/MVVM/MVI

下一步看《MVVM 模式》——它用"双向绑定"解决 MVC 里 View 要手动更新的问题,是 Vue、React、Android DataBinding 的核心思想。