Spring MVC 是 Spring Framework 提供的一个基于 Model-View-Controller (MVC) 模式的 Web 框架。它用于构建灵活且可扩展的 Web 应用程序。Spring MVC 将应用程序的业务逻辑、用户界面和导航逻辑分开,从而简化开发过程,提高代码的可维护性和可测试性。以下是对 Spring MVC 的基本原理和工作流程的详细讲解。
在 Spring MVC 中,DispatcherServlet
是一个核心组件,它充当前端控制器(Front Controller),负责将请求分发到适当的处理程序(Controller)。它的作用包括:
HandlerMapping
寻找请求的合适处理器。HandlerAdapter
调用处理器。ViewResolver
进行视图解析。Spring MVC 框架中的几个核心组件及其作用:
客户端发送请求到服务器,DispatcherServlet
作为中央调度器,接收所有的请求。DispatcherServlet
的配置是在 web.xml
中定义的,通常会将所有请求映射到此 servlet 进行处理。
<servlet>
<servlet-name>dispatcherservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>/WEB-INF/spring-mvc-config.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
DispatcherServlet
调用 HandlerMapping
,根据请求的 URL 寻找相应的处理器(Controller)。Spring MVC 支持多种 HandlerMapping
实现,如 BeanNameUrlHandlerMapping
、DefaultAnnotationHandlerMapping
等。
@Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
一旦找到合适的处理器,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);
}
}
处理器返回的 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>
视图渲染阶段,View
对象使用模型数据进行渲染,生成最终的 HTML 响应。响应被返回给 DispatcherServlet
,并最终发送给客户端。
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 开发一个简单的 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
我们需要在 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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>spring-mvc-exampleartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.12version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>javax.servlet.jsp-apiartifactId>
<version>2.3.3version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
<version>9.0.54version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.3.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifact
Id>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-war-pluginartifactId>
<version>3.3.1version>
<configuration>
<warSourceDirectory>src/main/webappwarSourceDirectory>
<failOnMissingWebXml>falsefailOnMissingWebXml>
configuration>
plugin>
plugins>
build>
project>
在 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">
<servlet>
<servlet-name>dispatcherservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>/WEB-INF/spring-mvc-config.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
在 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);
}
}
在 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;
}
}
在 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", "[email protected]"));
users.add(new User(2, "Jane Doe", "[email protected]"));
}
@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);
}
}
在 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";
}
}
在 WEB-INF/views
目录下创建视图文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
User List
User List
ID
Name
Email
Actions
${user.id}
${user.name}
${user.email}
Edit |
Delete
Add New User
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
User Form
User Form
Back to User List
使用 Tomcat 或其他 Servlet 容器部署应用程序,访问以下 URL 查看用户列表:
http://localhost:8080/users
- 用户列表http://localhost:8080/users/add
- 添加新用户通过以上示例,我们可以看到 Spring MVC 的核心工作流程和组件
如何协作完成一个完整的请求处理过程。在开发过程中,我们可以根据需要扩展和定制各个组件,以适应具体的业务需求。Spring MVC 提供了高度的灵活性和可扩展性,是现代 Web 应用开发的强大工具。