Spring MVC 基础 - 从零构建企业级Web应用
引言:为什么选择 Spring MVC?
Spring MVC 是 Spring 生态系统中构建 企业级 Web 应用的核心框架,其基于 MVC 设计模式,天然支持模块化开发、RESTful API、数据绑定等特性。
核心优势:
-
松耦合架构:模型(Model)、视图(View)、控制器(Controller)分离,便于团队协作和代码维护。
-
高度可扩展:支持 Thymeleaf、Freemarker 等视图技术,兼容 JSON/XML 等多种数据格式。
-
无缝整合:与 Spring IoC、AOP、Security 等模块深度集成,轻松实现复杂业务需求。
一、Spring MVC 核心架构解析
1. 核心组件与请求流程
Spring MVC 的请求处理流程可概括为以下步骤:
1. 用户发起请求 →
2. DispatcherServlet(前端控制器)接收请求 →
3. 调用 HandlerMapping 解析请求对应的 Controller →
4. Controller 处理业务逻辑,返回 Model 和视图名 →
5. ViewResolver 解析视图 →
6. 渲染视图并返回响应
关键组件:
-
DispatcherServlet:中央调度器,协调所有请求处理流程。将请求分发到相应的 Controller 进行处理。
-
HandlerMapping:映射请求 URL 到具体 Controller 方法。
-
ViewResolver:将逻辑视图名 (如 “home”) 解析为物理视图 (如 /WEB-INF/views/home.jsp)。
2. 模型-视图-控制器(MVC)详解
2.1 Model (模型层)
处理业务逻辑和数据部分。在 Spring MVC 中,通过 Model对象传递数据到视图层。
示例代码:
public class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}// Getter & Setterpublic String getName() { return name; }public void setName(String name) { this.name = name; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }
}
Controller 中,通过model.addAttribute传递数据。
model.addAttribute("user", new User("Alice", 25));
// 将 User 对象存入 Model,键为 "user",视图可通过 ${user} 获取
2.2 View (视图层)
负责展示数据,通过 JSP、Thymeleaf 等渲染数据。
<!-- Thymeleaf 视图层 -->
<div th:text="${user.name}">Username</div>
2.3 Controller(控制器)
处理用户HTTP请求,调用Service层业务逻辑,并返回数据给视图。
@GetMapping("/users")
public String listUsers(Model model) {model.addAttribute("users", userService.findAll());return "user-list"; // 返回视图名
}
二、实战:5步构建Spring MVC应用
以用户管理为例,完整构建一个Spring MVC应用,涵盖Controller、Service、DAO、Model以及视图层的实现。
步骤1:项目初始化(Maven/Gradle)
Maven 依赖配置
<dependencies><!-- Spring Web MVC:核心 Web 框架 --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.20</version></dependency><!-- Servlet API:用于处理 HTTP 请求 --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency><!-- 日志支持(SLF4J + Logback) --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.36</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version></dependency>
</dependencies>
步骤2:配置 DispatcherServlet
方式1.XML 配置(web.xml)
<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.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>
解析:
-
DispatcherServlet是 Spring MVC 的前端控制器,负责拦截所有请求。
-
contextConfigLocation指定 Spring 配置文件。
方式2.Java Config(推荐)
使用 Java 配置方式初始化 Spring MVC 应用,替代web.xml进行配置。
public class WebAppInitializer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) {AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();context.register(AppConfig.class);ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(context));dispatcher.setLoadOnStartup(1);dispatcher.addMapping("/");}
}
解析:
1.WebApplicationInitializer接口
用于配置 Web 应用,替代 XML 的 web.xml。
在应用启动时自动执行,相当于web.xml的和配置。
2. onStartup(ServletContext servletContext) 方法
在应用启动时执行,注册DispatcherServlet并进行 Spring 容器初始化。
3. AnnotationConfigWebApplicationContext
Spring 的基于 Java 配置(Annotation Config)的应用上下文。
4. DispatcherServlet
Spring MVC 的前端控制器 ,负责处理所有的 HTTP 请求。
5. setLoadOnStartup(1) 设置DispatcherServlet的加载优先级。
• 1表示服务器启动时立即加载,类似于web.xml里的1。
• 提前初始化 Spring 容器,提升响应速度。
总结:
1.替代 web.xml 进行 Spring MVC 配置,使用 Java 代码初始化 Web 应用。
2.注册 DispatcherServlet,拦截所有请求(/),并交给 Spring MVC 处理。
3.加载 Spring 配置类 AppConfig,在其中配置Controller、ViewResolver等组件。
4.优先加载 DispatcherServlet,提高应用启动速度。
步骤3:创建 Model、Controller、Service 层
1. 定义用户实体类(Model)
public class User {private String name;private int age;// 构造方法public User(String name, int age) {this.name = name;this.age = age;}// Getter & Setterpublic String getName() { return name; }public void setName(String name) { this.name = name; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }
}
解析:
-
User类是数据模型,存储用户基本信息。
-
getter和 setter方法用于数据访问和修改。
2. 创建 Service 层(业务逻辑)
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;@Service
public class UserService {public List<User> findAll() {return Arrays.asList(new User("Alice", 25),new User("Bob", 30));}
}
解析:
-
@Service注解表示该类是业务逻辑层。
-
findAll()方法返回用户列表。
3. 创建 Controller 层
@Controller
public class UserController {@Autowiredprivate UserService userService;// 处理 GET /users 请求@GetMapping("/users")public String listUsers(Model model) {model.addAttribute("users", userService.findAll());return "users"; // 返回视图名 users.jsp}// 处理 POST /users 请求@PostMapping("/users")public String createUser(@ModelAttribute User user) {userService.save(user);return "redirect:/users"; // 重定向到列表页}
}
解析:
-
@Controller标注控制器。
-
@GetMapping(“/users”)处理 /users请求。
model.addAttribute("users", userService.findAll());将数据传递给视图。
步骤4:视图解析器配置
方式1.XML 配置(spring-mvc.xml)
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/"/><property name="suffix" value=".jsp"/>
</bean>
方式2.Java Config
@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {@Beanpublic ViewResolver viewResolver() {InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix("/WEB-INF/views/");resolver.setSuffix(".jsp");return resolver;}
}
步骤5:视图开发与数据渲染
JSP 视图(users.jsp)
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head><title>用户列表</title>
</head>
<body><h1>用户列表</h1><table><tr><th>姓名</th><th>年龄</th></tr><c:forEach items="${users}" var="user"><tr><td>${user.name}</td><td>${user.age}</td></tr></c:forEach></table>
</body>
</html>
解析:
-
${users}变量来自 model.addAttribute(“users”, userService.findAll());
-
<c:forEach>遍历用户列表,展示姓名和年龄。
至此,一个完整的 Spring MVC Web 项目已经搭建完成,包含 Model、Service、Controller、View 及 DispatcherServlet 配置。你可以使用 Tomcat或 Spring Boot运行该项目,访问 /users看到用户列表页面。
三、进阶技巧与避坑指南
1. 常见问题排查
在开发 Spring MVC 应用时,可能会遇到一些常见问题,以下是几种典型情况及其排查方法:
Q1: 404 错误(页面找不到)
可能原因:
• @RequestMapping路径配置错误
• 请求 URL 与Controller映射不匹配
• DispatcherServlet配置有误
解决方案:
• 确认 Controller 是否正确映射路径
@Controller
@RequestMapping("/user")
public class UserController {@GetMapping("/list")public String userList() {return "userList"; // 对应 userList.jsp / userList.html}
}
问题示例:如果访问http://localhost:8080/list,但@RequestMapping(“/user”),则路径不匹配,应访问http://localhost:8080/user/list。
• 检查 web.xml 或 SpringBootApplication 是否正确配置
<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
Q2: 视图渲染失败
可能原因:
• 视图解析器prefix和suffix配置错误
• 视图文件未正确存放
解决方案:
• 检查 spring-mvc.xml 视图解析器配置
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/" /><property name="suffix" value=".jsp" />
</bean>
• prefix:设定视图文件的存放目录,例如/WEB-INF/views/
• suffix:设定视图的文件后缀,如.jsp或.html
• 确保视图文件存在
• 确保WEB-INF/views/userList.jsp存在,否则返回404。
Q3: 数据未传递到视图
可能原因:
• Controller没有正确添加数据到Model
• 视图未正确读取Model数据
解决方案:
• Controller 确保数据正确传递
@Controller
public class UserController {@GetMapping("/userList")public String userList(Model model) {List<User> users = userService.findAll();model.addAttribute("users", users);return "userList"; // 视图名称,对应 userList.jsp / userList.html}
}
• 前端正确获取数据(JSP 示例)
<c:forEach var="user" items="${users}"><p>${user.name} - ${user.email}</p>
</c:forEach>
如果model.addAttribute(“users”, users);这步缺失,则users为空,页面不会正确显示数据。
2. 扩展能力
2.1 RESTful API 支持
Spring MVC 结合 @RestController 实现 RESTful API,可以直接返回 JSON 数据。
示例代码
@RestController
@RequestMapping("/api")
public class ApiController {@GetMapping("/users")public List<User> getUsers() {return userService.findAll();}
}
• @RestController:等价于@Controller + @ResponseBody,直接返回 JSON,不经过视图解析器。
• @GetMapping(“/api/users”):定义GET请求,返回所有用户数据。
请求 & 响应示例
GET http://localhost:8080/api/users
[{"id":1, "name":"Alice", "email":"alice@example.com"},{"id":2, "name":"Bob", "email":"bob@example.com"}
]
2.2 表单验证
使用 Hibernate Validator 结合 @Valid 进行数据校验,可以确保用户提交的数据符合要求。
示例代码:
定义 User 实体类
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;public class User {private Long id;@NotBlank(message = "用户名不能为空")@Size(min = 3, max = 20, message = "用户名长度必须在 3-20 之间")private String name;@Email(message = "邮箱格式不正确")private String email;// getter & setter
}
Controller 处理校验
@Controller
@RequestMapping("/users")
public class UserController {@PostMapping("/add")public String addUser(@Valid @ModelAttribute User user, BindingResult result, Model model) {if (result.hasErrors()) {return "userForm"; // 返回表单页面,显示错误信息}userService.save(user);return "redirect:/users/list";}
}
JSP 页面显示错误
用户名: ${errors.name}邮箱:<input type="text" name="email" value="${user.email}"/>
<span style="color:red">${errors.email}</span><button type="submit">提交</button>
关键点
• @Valid触发数据校验
• BindingResult result捕获校验错误
• errors.name&errors.email显示错误信息
3. 总结
• 404 问题:检查@RequestMapping,URL 访问路径匹配。
• 视图解析失败:确保prefix和suffix配置正确,视图文件存在。
• 数据未传递:model.addAttribute()传值,前端正确获取数据。
• REST API 扩展:使用@RestController,返回 JSON 数据。
• 表单校验:@Valid+ Hibernate Validator,确保输入数据合法。
四、总结
1. 核心要点
Spring MVC 核心架构
• 通过DispatcherServlet统一调度请求,实现MVC 分层。
• Controller处理业务逻辑,ViewResolver解析视图,Model负责数据传递。
配置方式
• 支持XML 配置和Java Config,推荐后者以提升可维护性。
数据交互
• 通过@ModelAttribute绑定表单数据,使用@RequestParam处理请求参数。
视图层支持
• 兼容JSP、Thymeleaf等视图技术,同时支持RESTful API开发。
异常与调试
• 掌握404、视图渲染失败、数据未传递等常见问题的排查方法。