Spring MVC 是 Spring Framework 提供的一个基于 Model-View-Controller (MVC) 模式的 Web 框架。它用于构建灵活且可扩展的 Web 应用程序。Spring MVC 将应用程序的业务逻辑、用户界面和导航逻辑分开,从而简化开发过程,提高代码的可维护性和可测试性。以下是对 Spring MVC 的基本原理和工作流程的详细讲解。
Spring MVC 基本原理
1. MVC 架构
- Model(模型):负责业务逻辑的处理和数据的传递,通常是 Java Bean 或 POJO 对象。
- View(视图):负责将数据展现给用户,通常是 JSP、Thymeleaf、FreeMarker 等模板引擎。
- Controller(控制器):负责处理用户请求,将请求分派给相应的模型和视图进行处理。
2. DispatcherServlet
在 Spring MVC 中,DispatcherServlet
是一个核心组件,它充当前端控制器(Front Controller),负责将请求分发到适当的处理程序(Controller)。它的作用包括:
- 接收所有 HTTP 请求。
- 通过
HandlerMapping
寻找请求的合适处理器。 - 使用
HandlerAdapter
调用处理器。 - 根据处理器的执行结果选择合适的
ViewResolver
进行视图解析。 - 返回响应给用户。
3. 组件概述
Spring MVC 框架中的几个核心组件及其作用:
- DispatcherServlet:负责接收并分派请求,是整个框架的核心。
- HandlerMapping:负责将请求映射到处理器(Controller)。
- Controller:处理业务逻辑并返回模型数据和视图信息。
- HandlerAdapter:对请求处理器进行适配,以便于调用。
- ViewResolver:将逻辑视图名解析为具体的视图实现。
- View:负责渲染模型数据,将结果返回给客户端。
Spring MVC 工作流程
1. 请求进入
客户端发送请求到服务器,DispatcherServlet
作为中央调度器,接收所有的请求。DispatcherServlet
的配置是在 web.xml
中定义的,通常会将所有请求映射到此 servlet 进行处理。
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2. 请求分派
DispatcherServlet
调用 HandlerMapping
,根据请求的 URL 寻找相应的处理器(Controller)。Spring MVC 支持多种 HandlerMapping
实现,如 BeanNameUrlHandlerMapping
、DefaultAnnotationHandlerMapping
等。
@Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
3. 处理请求
一旦找到合适的处理器,DispatcherServlet
使用 HandlerAdapter
调用处理器方法。HandlerAdapter
负责将请求和响应对象传递给处理器。
- 参数绑定:
HandlerAdapter
将 HTTP 请求参数绑定到处理器方法的参数上。 - 业务逻辑处理:处理器执行业务逻辑,通常会返回一个
ModelAndView
对象,包含模型数据和视图名称。
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/list")
public ModelAndView listUsers() {
List<User> users = userService.findAll();
return new ModelAndView("userList", "users", users);
}
}
4. 视图解析
处理器返回的 ModelAndView
对象被传递给 ViewResolver
进行视图解析。ViewResolver
将逻辑视图名称转换为具体的视图对象(如 JSP、Thymeleaf 模板)。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
5. 视图渲染
视图渲染阶段,View
对象使用模型数据进行渲染,生成最终的 HTML 响应。响应被返回给 DispatcherServlet
,并最终发送给客户端。
6. 返回响应
DispatcherServlet
将视图渲染的结果返回给客户端,完成整个请求处理过程。
下图展示了 Spring MVC 的整体工作流程:
Client Request
|
v
DispatcherServlet
|
v
HandlerMapping (找到处理器)
|
v
HandlerAdapter (调用处理器)
|
v
Controller/Handler (业务逻辑)
|
v
ModelAndView (返回结果)
|
v
ViewResolver (视图解析)
|
v
View (视图渲染)
|
v
Client Response
Spring MVC 实战示例
为了更好地理解 Spring MVC 的工作原理和流程,我们将通过一个具体的示例来演示如何使用 Spring MVC 开发一个简单的 Web 应用程序。
项目结构
我们将构建一个用户管理系统,展示用户列表,并提供用户的增删改查功能。项目结构如下:
spring-mvc-example/
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ ├── config
│ │ │ │ └── WebConfig.java
│ │ │ ├── controller
│ │ │ │ └── UserController.java
│ │ │ ├── model
│ │ │ │ └── User.java
│ │ │ └── service
│ │ │ ├── UserService.java
│ │ │ └── UserServiceImpl.java
│ │ └── resources
│ │ └── webapp
│ │ ├── WEB-INF
│ │ │ ├── views
│ │ │ │ ├── userList.jsp
│ │ │ │ ├── userForm.jsp
│ │ │ └── web.xml
│ └── pom.xml
1. 配置pom.xml
我们需要在 pom.xml
中添加 Spring MVC 和相关依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-mvc-example</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.12</version>
</dependency>
<!-- JSP 支持 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.54</version>
</dependency>
<!-- Spring Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.12</version>
<scope>test</scope>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifact
Id>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 配置 Web.xml
在 WEB-INF
目录下创建 web.xml
文件,配置 DispatcherServlet
:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Spring DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3. 创建 Spring MVC 配置类
在 com.example.config
包下创建 WebConfig.java
,配置 Spring MVC:
package com.example.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
registry.viewResolver(resolver);
}
}
4. 创建模型类
在 com.example.model
包下创建 User.java
:
package com.example.model;
public class User {
private int id;
private String name;
private String email;
public User() {}
public User(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and Setters
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
5. 创建服务类
在 com.example.service
包下创建 UserService.java
接口和 UserServiceImpl.java
实现类:
package com.example.service;
import com.example.model.User;
import java.util.List;
public interface UserService {
List<User> findAll();
User findById(int id);
void save(User user);
void deleteById(int id);
}
package com.example.service;
import com.example.model.User;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
private static List<User> users = new ArrayList<>();
static {
users.add(new User(1, "John Doe", "john@example.com"));
users.add(new User(2, "Jane Doe", "jane@example.com"));
}
@Override
public List<User> findAll() {
return users;
}
@Override
public User findById(int id) {
return users.stream()
.filter(user -> user.getId() == id)
.findFirst()
.orElse(null);
}
@Override
public void save(User user) {
users.add(user);
}
@Override
public void deleteById(int id) {
users.removeIf(user -> user.getId() == id);
}
}
6. 创建控制器类
在 com.example.controller
包下创建 UserController.java
:
package com.example.controller;
import com.example.model.User;
import com.example.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.*;
@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public String listUsers(Model model) {
model.addAttribute("users", userService.findAll());
return "userList";
}
@GetMapping("/{id}")
public String viewUser(@PathVariable int id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "userForm";
}
@GetMapping("/add")
public String addUserForm(Model model) {
model.addAttribute("user", new User());
return "userForm";
}
@PostMapping("/add")
public String saveUser(@ModelAttribute User user) {
userService.save(user);
return "redirect:/users";
}
@GetMapping("/delete/{id}")
public String deleteUser(@PathVariable int id) {
userService.deleteById(id);
return "redirect:/users";
}
}
7. 创建视图文件
在 WEB-INF/views
目录下创建视图文件:
7.1 userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>User List</title>
</head>
<body>
<h2>User List</h2>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
<c:forEach var="user" items="${users}">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.email}</td>
<td>
<a href="${pageContext.request.contextPath}/users/${user.id}">Edit</a> |
<a href="${pageContext.request.contextPath}/users/delete/${user.id}">Delete</a>
</td>
</tr>
</c:forEach>
</table>
<a href="${pageContext.request.contextPath}/users/add">Add New User</a>
</body>
</html>
7.2 userForm.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>User Form</title>
</head>
<body>
<h2>User Form</h2>
<form action="${pageContext.request.contextPath}/users/add" method="post">
<input type="hidden" name="id" value="${user.id}"/>
<table>
<tr>
<td>Name:</td>
<td><input type="text" name="name" value="${user.name}"/></td>
</tr>
<tr>
<td>Email:</td>
<td><input type="text" name="email" value="${user.email}"/></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Save"/></td>
</tr>
</table>
</form>
<a href="${pageContext.request.contextPath}/users">Back to User List</a>
</body>
</html>
8. 启动应用程序
使用 Tomcat 或其他 Servlet 容器部署应用程序,访问以下 URL 查看用户列表:
http://localhost:8080/users
- 用户列表http://localhost:8080/users/add
- 添加新用户
总结
通过以上示例,我们可以看到 Spring MVC 的核心工作流程和组件
如何协作完成一个完整的请求处理过程。在开发过程中,我们可以根据需要扩展和定制各个组件,以适应具体的业务需求。Spring MVC 提供了高度的灵活性和可扩展性,是现代 Web 应用开发的强大工具。