目录
- 什么是MVC模式? - 核心思想与定义
- 为什么在Java Web中使用MVC? - 优势与解决的问题
- MVC在Java Web中的具体角色 - Model, View, Controller 各司其职
- 一个典型的Java Web MVC请求流程 - 从用户输入到看到结果
- Java Web MVC的演进 - 从Servlet到Spring Framework
- 实践案例:一个简单的用户登录示例 - 代码与步骤
- 总结与展望
什么是MVC模式?
MVC是一种软件设计架构模式,旨在将一个应用程序划分为三个相互关联的部分,以实现关注点分离。

-
M - Model (模型):
- 职责: 负责数据和业务逻辑,它是应用程序的核心。
- 数据库操作、业务规则计算、数据状态管理等,它不关心数据如何被展示。
- 例子: 一个
User类,包含id,username,password等属性,以及一个UserService类负责处理用户注册、登录等逻辑。
-
V - View (视图):
- 职责: 负责数据显示和用户交互,它是用户看到并与之交互的界面。
- HTML, JSP, Thymeleaf, FreeMarker等模板文件,它只负责从
Model中获取数据并展示,不包含任何业务逻辑。 - 例子: 一个
login.jsp页面,包含用户名和密码的输入框,以及一个“登录”按钮。
-
C - Controller (控制器):
- 职责: 接收用户请求,调用
Model处理业务逻辑,然后选择一个View来展示结果,它是Model和View之间的协调者。 - 接收HTTP请求,解析参数,调用相应的服务(
Model),并将处理结果(数据)传递给View进行渲染。 - 例子: 一个
LoginServlet或LoginController,它接收登录请求,调用UserService验证用户,然后根据验证结果决定是跳转到欢迎页面还是登录失败页面。
- 职责: 接收用户请求,调用
核心思想: 用户通过 View 发起请求,Controller 接收并处理,Model 负责核心业务,Controller 选择一个 View 将结果呈现给用户,这三者各司其职,降低了代码的耦合度。

为什么在Java Web中使用MVC?
在MVC模式出现之前,Java Web开发常常是“Model 1”模式,即JSP页面中既包含HTML展示代码,又嵌入Java代码(脚本片段Scriptlets)来处理业务逻辑,这导致了:
- 代码混乱: HTML和Java代码混在一起,难以维护。
- 职责不清: JSP页面承担了太多责任,既管显示又管逻辑。
- 难以测试: 业务逻辑与视图紧密耦合,无法单独对业务逻辑进行单元测试。
MVC模式完美地解决了这些问题:
- 职责分离: 代码结构清晰,
Model,View,Controller各司其职。 - 可维护性高: 修改业务逻辑不会影响视图,修改视图也不会影响业务逻辑。
- 可复用性强:
Model的业务逻辑可以被不同的View(例如网页、App接口)复用。 - 可扩展性好: 可以轻松地更换视图技术(例如从JSP换成Thymeleaf)或增加新的控制器。
- 易于测试:
Model的业务逻辑可以独立进行单元测试。
MVC在Java Web中的具体角色
我们来更具体地看看这三个角色在Java Web技术栈中通常对应什么。
| 角色 | 职责 | 常见技术实现 |
|---|---|---|
| Model (模型) | - 数据对象: POJO (Plain Old Java Object),如 User.java, Product.java。- 业务逻辑/服务: 处理核心业务,如 UserService.java, OrderService.java。- 数据访问层: 与数据库交互,如 UserDAO.java (Data Access Object)。 |
- JavaBeans/POJOs - Spring Service (带有 @Service 注解)- JPA/Hibernate Entities (带有 @Entity 注解)- MyBatis Mapper |
| View (视图) | - 渲染数据: 接收来自Controller的数据,并将其转换为用户可见的格式(HTML、JSON等)。 - 提供用户界面: 包含HTML、CSS、JavaScript等。 |
- JSP / JSTL: 经典的Java视图技术。 - Thymeleaf: 现代化的、优雅的服务器端模板引擎。 - FreeMarker: 另一个流行的模板引擎。 - JSON/XML: 用于构建RESTful API的视图。 |
| Controller (控制器) | - 请求入口: 接收所有来自客户端的HTTP请求。 - 参数解析: 从请求中提取参数(如URL路径、查询参数、表单数据)。 - 业务调度: 调用Model中的业务逻辑方法处理请求。 - 结果响应: 根据业务处理结果,选择合适的View进行响应,或直接返回数据(如JSON)。 |
- Servlet: 最原始、最底层的控制器实现。 - Spring MVC Controller (带有 @Controller 注解): 现代Java Web开发的主流,功能强大,使用方便。 |
一个典型的Java Web MVC请求流程
假设用户点击了“登录”按钮,一个典型的MVC请求流程如下:

-
用户发起请求: 用户在浏览器中填写登录表单并点击提交,浏览器向服务器发送一个HTTP POST请求,
http://example.com/login。 -
Controller接收请求: 前端控制器(在Spring中是
DispatcherServlet)接收到这个请求,它会根据URL配置,将请求转发给相应的Controller方法(LoginController中的handleLogin方法)。 -
Controller处理请求:
Controller方法从HTTP请求中获取用户名和密码参数。- 它不自己去验证用户,而是调用
Model层的UserService,并传入用户名和密码。UserService userService = new UserService(); User user = userService.login(username, password);
UserService执行数据库查询和密码比对等业务逻辑,然后将结果(User对象或null)返回给Controller。
-
Controller选择View:
Controller根据UserService返回的结果做决定。- 如果登录成功:
- 它可以将用户信息存入
Session。 - 它选择一个“欢迎页”作为
View,并将用户信息作为数据传递给这个View。
- 它可以将用户信息存入
- 如果登录失败:
- 它选择一个“登录页”作为
View,并将一个错误信息(如“用户名或密码错误”)传递给这个View。
- 它选择一个“登录页”作为
-
View渲染响应:
- 选定的
View(例如一个JSP页面)接收到从Controller传来的数据。 - 它使用JSTL或EL表达式等技术,将数据动态地嵌入到HTML模板中。
- 欢迎页可能会显示“欢迎您,${user.username}!”。
View生成完整的HTML文档。
- 选定的
-
服务器响应浏览器: 服务器将生成的HTML文档作为HTTP响应发送回用户的浏览器,浏览器解析并渲染HTML,用户最终看到登录成功或失败的页面。
Java Web MVC的演进
Java Web MVC的实现方式随着技术的发展不断演进:
-
原生Servlet (早期)
- 特点: 所有的控制器逻辑都写在
doGet()和doPost()方法中,需要手动获取参数、手动调用业务逻辑、手动跳转页面。 - 缺点: 代码冗余,配置繁琐,一个Servlet只能处理一个功能,扩展性差。
- 特点: 所有的控制器逻辑都写在
-
MVC框架 (如Struts1, Struts2)
- 特点: 引入了前端控制器模式,所有请求都先到一个核心控制器,再由它分发到具体的业务Action(控制器),提供了更清晰的MVC结构和更丰富的功能。
- 缺点: 配置文件复杂,侵入性强(代码需要继承特定框架的类),学习曲线陡峭。
-
现代MVC框架 (如Spring MVC)
- 特点: 基于注解(Annotation)驱动,配置非常简洁(通常只需在方法上加上
@RequestMapping),控制器是普通的POJO,耦合度极低,与Spring IoC容器无缝集成,依赖注入非常方便。 - 现状: 这是目前Java Web开发的主流和标准,Spring Boot更是极大地简化了Spring MVC项目的创建和配置。
- 特点: 基于注解(Annotation)驱动,配置非常简洁(通常只需在方法上加上
实践案例:一个简单的用户登录示例
我们使用 Spring MVC 来构建一个简单的登录功能。
项目结构:
src
└── main
├── java
│ └── com.example.demo
│ ├── controller
│ │ └── LoginController.java
│ ├── model
│ │ ├── User.java
│ │ └── service
│ │ └── UserService.java
│ └── DemoApplication.java
├── resources
│ └── application.properties
└── webapp
├── WEB-INF
│ └── views
│ ├── home.jsp
│ └── login.jsp
└── index.jsp
步骤1: Model (模型)
User.java (数据对象)
package com.example.demo.model;
public class User {
private String username;
private String password;
// Getters and Setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
UserService.java (业务逻辑)
package com.example.demo.model.service;
import com.example.demo.model.User;
import org.springframework.stereotype.Service;
@Service // 告诉Spring这是一个服务类,需要被管理
public class UserService {
public User login(String username, String password) {
// 这里是模拟的业务逻辑,实际项目中会查询数据库
if ("admin".equals(username) && "password123".equals(password)) {
User user = new User();
user.setUsername(username);
return user;
}
return null; // 登录失败
}
}
步骤2: View (视图)
login.jsp (登录页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="/login" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
home.jsp (登录成功后的首页)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>首页</title>
</head>
<body>
<h1>欢迎您, ${user.username}!</h1>
</body>
</html>
步骤3: Controller (控制器)
LoginController.java
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.model.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller // 告诉Spring这是一个控制器
public class LoginController {
@Autowired // 自动注入UserService,由Spring IoC容器提供实例
private UserService userService;
// 处理登录页面的GET请求,直接显示登录页
@GetMapping("/login")
public String showLoginPage() {
return "login"; // 返回视图名,Spring会解析为 /WEB-INF/views/login.jsp
}
// 处理登录表单的POST请求
@PostMapping("/login")
public String handleLogin(
@RequestParam("username") String username,
@RequestParam("password") String password,
Model model) { // Model用于向视图传递数据
User user = userService.login(username, password);
if (user != null) {
// 登录成功,将用户信息存入Model
model.addAttribute("user", user);
return "home"; // 跳转到首页
} else {
// 登录失败,可以向Model中添加错误信息,并返回登录页
model.addAttribute("error", "用户名或密码错误");
return "login";
}
}
}
步骤4: 配置与启动
application.properties (Spring Boot配置)
# 服务器端口 server.port=8080 # 视图解析器配置 spring.mvc.view.prefix=/WEB-INF/views/ spring.mvc.view.suffix=.jsp
DemoApplication.java (Spring Boot启动类)
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
运行流程总结:
- 启动
DemoApplication,Spring Boot启动内嵌Tomcat服务器。 - 用户访问
http://localhost:8080/login。 LoginController.showLoginPage()被执行,返回"login"。- Spring的视图解析器找到
/WEB-INF/views/login.jsp并渲染,用户看到登录页面。 - 用户输入
admin/password123并提交表单。 - 请求以POST方式发送到
/login。 LoginController.handleLogin()被执行。userService.login()被调用,返回一个User对象。handleLogin()将User对象存入Model,并返回"home"。- Spring视图解析器找到
/WEB-INF/views/home.jsp,使用Model中的数据渲染页面。 - 浏览器收到渲染后的HTML,显示“欢迎您, admin!”。
总结与展望
基于MVC的Java Web开发是一种成熟、强大且结构化的开发模式,它通过将应用程序分解为Model、View和Controller三个部分,实现了高度的职责分离,使得代码更易于理解、维护、测试和扩展,从原始的Servlet到现代的Spring MVC,该模式的核心思想一直被传承和发扬,是每一位Java Web开发者必须掌握的核心知识。
展望: 虽然MVC模式非常经典,但现代Web开发也在不断演进:
- 前后端分离: 越来越多的项目采用前后端分离架构。
View层完全由前端框架(如Vue.js, React, Angular)负责,运行在浏览器中,后端的Controller和Model则通过提供RESTful API(通常返回JSON数据)来与前端通信,这种模式下,后端的MVC模式依然存在,但View的角色被API接口取代。 - 新架构模式: 对于一些复杂的单页应用,一些新的架构模式如MVVM (Model-View-ViewModel) 也开始流行,它进一步增强了View和Model的解耦。
尽管如此,MVC所蕴含的“关注点分离”的哲学思想,依然是现代软件设计的基石,掌握MVC,是理解更复杂架构的第一步。
