Spring Boot 对 Spring MVC 的支持可以说是开箱即用的,只要引入了 spring-boot-starter-web 依赖,内嵌的 Tomcat 和 DispatcherServlet 就会自动装配完成。但很多初学者虽然能跑通一个简单的 Hello World,却对整个 MVC 的请求流转过程和分层结构的组织方式缺乏系统认识,导致项目一旦变大就变得混乱。本文将从依赖配置、三层代码组织、视图渲染、MVC 扩展配置四个方面,完整演示如何在 Spring Boot 中实现规范的 MVC 架构。

一、引入依赖并理解自动装配原理
创建一个标准的 Spring Boot 项目后,在 pom.xml 中加入 web 依赖即可,这是整个 MVC 体系的入口:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</;/artifactId>
</dependency>这个 starter 会传递引入 spring-web、spring-webmvc、tomcat-embed-core 等核心组件。Spring Boot 在启动时通过 SpringBootServletInitializer 和 DispatcherServletAutoConfiguration 自动注册了 DispatcherServlet,并把它映射到 / 路径上,也就是说我们不再需要像传统 SSM 项目那样在 web.xml 中手工配置前端控制器。
理解请求流转过程对排查问题非常有帮助。一次完整的请求会经历这样几个环节:浏览器发起请求后首先到达 DispatcherServlet,它通过 HandlerMapping 找到对应的 Controller 方法,再经过拦截器的 preHandle 处理,然后通过 HandlerAdapter 完成参数绑定与方法调用,Controller 返回的结果由 ViewResolver 或 HttpMessageConverter 处理,最终渲染出响应。搞清楚这条链路,后面配置拦截器或视图解析器时就知道各自的介入时机了。
二、搭建规范的 Controller、Service、Repository 三层结构
MVC 架构的核心思想是分层解耦。Controller 只负责接收请求和返回响应,不写业务逻辑;Service 承担业务处理;Repository 负责数据访问。下面以一个用户管理模块为例来演示完整的三层代码。
首先定义实体类和简单的数据访问层,这里为了演示方便用内存 Map 模拟数据库:
@Entity
public class User {
private Long id;
private String username;
private String email;
// 省略 getter 和 setter
}
@Repository
public class UserRepository {
private static final Map<Long, User> store = new ConcurrentHashMap<>();
private static final AtomicLong idGen = new AtomicLong(1);
public User save(User user) {
user.setId(idGen.getAndIncrement());
store.put(user.getId(), user);
return user;
}
public User findById(Long id) {
return store.get(id);
}
public List<User> findAll() {
return new ArrayList<>(store.values());
}
}接着是 Service 层,它负责组织业务规则,比如校验用户名是否重复:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
if (user.getUsername() == null || user.getUsername().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
return userRepository.save(user);
}
public User getUser(Long id) {
User user = userRepository.findById(id);
if (user == null) {
throw new RuntimeException("用户不存在");
}
return user;
}
}最后是 Controller 层,使用 @RestController 返回 JSON 数据,或者用 @Controller 配合模板引擎渲染页面:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public User create(@RequestBody User user) {
return userService.createUser(user);
}
@GetMapping("/{id}")
public User detail(@PathVariable Long id) {
return userService.getUser(id);
}
@GetMapping
public List<User> list() {
return userService.findAll();
}
}这种分层的价值在于可测试性和可维护性。Service 层不依赖任何 Web 组件,可以单独做单元测试;将来替换数据来源,比如从内存 Map 换成 MySQL 或 MongoDB,只需要改动 Repository 层的实现,上层代码完全不受影响。
三、整合 Thymeleaf 实现视图渲染
如果项目需要返回 HTML 页面而不是 JSON,就需要引入模板引擎。Spring Boot 官方推荐 Thymeleaf,加入依赖后把模板文件放到 src/main/resources/templates 目录下即可被自动识别:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>编写一个返回页面的 Controller,方法返回值就是模板名称,Model 中存放的数据可以直接在模板里访问:
@Controller
public class PageController {
@Autowired
private UserService userService;
@GetMapping("/users")
public String userListPage(Model model) {
model.addAttribute("users", userService.findAll());
return "user-list";
}
}模板文件 user-list.html 中通过 Thymeleaf 的命名空间标签遍历数据:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>用户列表</title>
</head>
<body>
<table>
<tr th:each="u : ${users}">
<td th:text="${u.id}"></td>
<td th:text="${u.username}"></td>
<td th:text="${u.email}"></td>
</tr>
</table>
</body>
</html>开发阶段建议在 application.yml 中关闭模板缓存,修改页面后刷新即可看到效果,不需要重启应用:
spring:
thymeleaf:
cache: false四、通过 WebMvcConfigurer 扩展 MVC 配置
Spring Boot 提供了 WebMvcConfigurer 接口让开发者以编码的方式扩展 MVC 能力,而不必破坏自动配置。常见的扩展点包括拦截器注册、跨域配置、静态资源映射和视图控制器等。写一个配置类实现该接口即可:
@Configuration
public class WebConfig implements WebMvcConfigurer {
// 注册拦截器,拦截 /api/** 下的所有请求
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/login");
}
// 全局跨域配置
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8081")
.allowedMethods("GET", "POST", "PUT", "DELETE");
}
// 自定义静态资源映射
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}拦截器的实现也很简单,继承 HandlerInterceptor 接口并重写相应方法:
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
System.out.println("请求进入: " + request.getRequestURI());
return true; // 返回 false 会中断请求
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
System.out.println("请求完成, 状态码: " + response.getStatus());
}
}需要注意一点:如果配置类上加的是 @EnableWebMvc 注解,Spring Boot 的 MVC 自动配置会全部失效,所有组件都得自己提供,除非有特殊定制需求,一般不建议使用。实现 WebMvcConfigurer 则是在保留自动配置的基础上做增量扩展,这是两种方式的本质区别。
五、补充全局异常处理让响应更规范
实际项目中,Service 抛出的异常如果直接暴露给前端会非常不友好。可以借助 @RestControllerAdvice 做统一异常处理,把异常转换为规范的 JSON 结构:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public Map<String, Object> handleIllegalArg(IllegalArgumentException e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 400);
result.put("message", e.getMessage());
return result;
}
@ExceptionHandler(Exception.class)
public Map<String, Object> handleException(Exception e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 500);
result.put("message", "服务器内部错误");
return result;
}
}这样无论哪一层抛出异常,前端拿到的都是统一格式的响应体,日志中也能定位到具体堆栈。配合前面讲的三层结构和拦截器配置,一个结构清晰、职责分明的 Spring Boot MVC 项目骨架就搭建完成了。后续如果需要接入真实的数据库,只需替换 Repository 层为 MyBatis 或 Spring Data JPA 实现,整体架构不需要任何调整,这正是分层设计带来的好处。
Spring Boot整合WebMVCMVC架构Spring MVC配置修改时间:2026-09-08 14:35:20