乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 SpringMVC概述与DispatcherServlet

    • 本章导读:Spring MVC概述与DispatcherServlet
    • Spring MVC 是什么
    • MVC 设计模式
    • 前端控制器模式
    • DispatcherServlet
    • 核心组件协作
  • 第2章 控制器与请求映射

    • 本章导读:控制器与请求映射
    • Controller
    • RestController
    • RequestMapping
    • GetMapping
    • PostMapping
    • PutMapping
    • DeleteMapping
    • PathVariable
    • RESTful
    • 请求映射原理
  • 第3章 请求参数获取与转换

    • 本章导读:请求参数获取与转换
    • RequestParam
    • RequestBody
    • RequestHeader
    • CookieValue
    • Model
    • ModelAttribute
    • 数据绑定原理
    • 数据校验
  • 第4章 响应数据与视图解析

    • 本章导读:响应数据与视图解析
    • ResponseBody
    • ResponseEntity
    • ModelAndView
    • ViewResolver
    • HttpMessageConverter
    • forward与redirect
  • 第5章 拦截器过滤器与跨域

    • 本章导读:拦截器、过滤器与跨域
    • HandlerInterceptor
    • WebMvcConfigurer
    • CrossOrigin
    • 登录验证实战
  • 第6章 文件上传与异常处理

    • 本章导读:文件上传与异常处理
    • MultipartFile
    • 文件下载
    • ExceptionHandler
    • ControllerAdvice
    • RestControllerAdvice
    • ResponseStatus
  • 第7章 高级特性与最佳实践

    • 本章导读:高级特性与最佳实践
    • SessionAttributes
    • SessionAttribute
    • RedirectAttributes
    • MockMvc测试
    • 国际化
    • 最佳实践
  • 第8章 扩展与异步机制

    • 本章导读:扩展与异步机制
    • 异步请求处理
    • 自定义参数解析器
    • 内容协商

WebMvcConfigurer

本章聚焦 Spring MVC 的配置扩展层。前面章节讲解了如何编写 Controller、如何定义拦截器、如何进行消息转换,但这些组件都需要被"注册"到 Spring MVC 中才能生效。WebMvcConfigurer 是 Spring MVC 提供的核心配置接口,允许开发者以声明式方式定制框架行为,而无需继承或修改底层类。

本章与全局的关系:前面章节讲解了 HandlerInterceptor 的编写,本章讲解如何把拦截器注册到 Spring MVC。同时涵盖视图控制器、静态资源、跨域等常见配置场景。


定义与作用

WebMvcConfigurer 是 Spring MVC 提供的配置回调接口,定义了一系列 addXxx / configureXxx 方法,允许开发者在 Spring MVC 初始化时注入自定义配置。它采用"回调注册"模式——Spring MVC 在启动时会收集所有实现了该接口的 Bean,按顺序调用其配置方法。

核心职责包括:

  1. 注册拦截器(addInterceptors)——指定拦截器生效的路径和排除的路径
  2. 配置视图控制器(addViewControllers)——无需编写 Controller 即可映射 URL 到视图
  3. 配置静态资源(addResourceHandlers)——指定静态资源的访问路径和存放位置
  4. 配置跨域(addCorsMappings)——全局 CORS 策略
  5. 配置消息转换器(configureMessageConverters / extendMessageConverters)——自定义 HttpMessageConverter
  6. 配置参数解析器(addArgumentResolvers)——自定义 Controller 方法参数解析
  7. 配置返回值处理器(addReturnValueHandlers)——自定义返回值处理

生活类比:公司行政部的服务窗口

想象飞翔科技的行政部:

  • 行政部(WebMvcConfigurer)不直接处理业务,但提供一系列服务窗口
  • 你想申请门禁卡(注册拦截器)?去 3 号窗口
  • 你想申请停车位(配置静态资源)?去 5 号窗口
  • 你想申请出差报销(配置跨域)?去 7 号窗口
  • 每个窗口都有标准化的表格和流程,你只需要填表提交,行政部统一处理

关键认知:WebMvcConfigurer 是"配置入口",不是"配置本身"。真正的配置逻辑(如拦截器实现、资源位置)由开发者提供,WebMvcConfigurer 负责把它们挂接到 Spring MVC 的运行时。


核心原理

配置类结构

工作流程:

  1. Spring 容器启动时,扫描所有 @Configuration 类
  2. 发现实现了 WebMvcConfigurer 的 Bean,收集其配置
  3. DelegatingWebMvcConfiguration 汇总所有配置,生成最终的 Spring MVC 运行时组件
  4. DispatcherServlet 使用这些组件处理请求

拦截器注册流程


适用位置与常用属性

WebMvcConfigurer 是一个接口,开发者创建 @Configuration 类实现该接口即可生效:

方法作用典型场景
addInterceptors(InterceptorRegistry registry)注册拦截器登录检查、权限校验、日志记录
addViewControllers(ViewControllerRegistry registry)直接映射 URL 到视图/ → index.html,错误页面
addResourceHandlers(ResourceHandlerRegistry registry)配置静态资源映射/static/** → classpath:/static/
addCorsMappings(CorsRegistry registry)配置全局跨域策略前后端分离项目的 CORS
configureMessageConverters(List<HttpMessageConverter<?>> converters)配置消息转换器添加自定义 YAML/Protobuf 转换器
extendMessageConverters(List<HttpMessageConverter<?>> converters)扩展消息转换器在默认转换器基础上追加
addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)自定义参数解析器解析自定义注解参数
addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers)自定义返回值处理器统一包装 REST 响应
configureViewResolvers(ViewResolverRegistry registry)配置视图解析器Thymeleaf、JSP 解析器
configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)启用默认 Servlet处理静态资源回退

InterceptorRegistration 链式 API

方法作用
addInterceptor(HandlerInterceptor)添加拦截器实例
addPathPatterns(String... patterns)指定拦截的 URL 模式(如 /**, /api/**)
excludePathPatterns(String... patterns)指定排除的 URL 模式(如 /login, /static/**)
order(int order)指定拦截器顺序(数字越小越先执行)

完整示例

场景

飞翔科技员工管理系统需要以下配置:

  1. 注册三个拦截器:登录检查(拦截所有路径,排除登录注册)、权限校验(仅拦截 /admin/**)、日志记录(拦截所有路径)
  2. 配置首页直接跳转到 index.html
  3. 配置静态资源映射 /uploads/** 到文件系统目录
  4. 配置全局跨域,允许前端开发服务器 http://localhost:3000 访问

项目结构

employee-web/
├── src/main/java/
│   └── com/feixiang/web/
│       ├── interceptor/
│       │   ├── LoginInterceptor.java
│       │   ├── AdminAuthInterceptor.java
│       │   └── LogInterceptor.java
│       └── config/
│           └── WebConfig.java

配置类实现

// WebConfig.java
package com.feixiang.web.config;

import com.feixiang.web.interceptor.AdminAuthInterceptor;
import com.feixiang.web.interceptor.LoginInterceptor;
import com.feixiang.web.interceptor.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final LoginInterceptor loginInterceptor;
    private final AdminAuthInterceptor adminAuthInterceptor;
    private final LogInterceptor logInterceptor;

    public WebConfig(LoginInterceptor loginInterceptor,
                     AdminAuthInterceptor adminAuthInterceptor,
                     LogInterceptor logInterceptor) {
        this.loginInterceptor = loginInterceptor;
        this.adminAuthInterceptor = adminAuthInterceptor;
        this.logInterceptor = logInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 登录检查拦截器:拦截所有请求,排除登录、注册、静态资源
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/register", "/static/**", "/error")
                .order(1);

        // 权限校验拦截器:仅拦截管理员路径
        registry.addInterceptor(adminAuthInterceptor)
                .addPathPatterns("/admin/**")
                .order(2);

        // 日志拦截器:拦截所有请求
        registry.addInterceptor(logInterceptor)
                .addPathPatterns("/**")
                .order(3);
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 首页直接映射到 index.html
        registry.addViewController("/").setViewName("index");
        // 错误页面映射
        registry.addViewController("/error/404").setViewName("404");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 文件上传目录映射为可访问的静态资源
        registry.addResourceHandler("/uploads/**")
                .addResourceLocations("file:/var/feixiang/uploads/");
        
        // 额外的静态资源路径
        registry.addResourceHandler("/assets/**")
                .addResourceLocations("classpath:/assets/");
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 全局跨域配置
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

HTTP 请求示例 1:正常业务请求通过拦截器链

$ curl -X GET http://localhost:8080/api/employees \
  -b cookies.txt \
  -v

响应:

< HTTP/1.1 200
< Content-Type: application/json
<
[{"id":1,"name":"张三"}]

控制台输出:

[LoginInterceptor] preHandle: /api/employees → true
[LogInterceptor] preHandle: /api/employees → true
[LogInterceptor] afterCompletion: /api/employees, 耗时=15ms
[LoginInterceptor] afterCompletion: /api/employees

流程解析:

  1. 请求 /api/employees 匹配 /**,三个拦截器都参与
  2. 按 order 顺序:LoginInterceptor(1) → AdminAuthInterceptor 不匹配路径(跳过)→ LogInterceptor(3)
  3. LoginInterceptor 检查 session,已登录,返回 true
  4. LogInterceptor 记录开始时间,返回 true
  5. Controller 执行
  6. afterCompletion 逆序执行:LogInterceptor → LoginInterceptor

HTTP 请求示例 2:排除路径绕过登录检查

$ curl -X GET http://localhost:8080/login

响应:

< HTTP/1.1 200
< Content-Type: text/html
<
<!DOCTYPE html>...登录页面...

流程解析:

  • /login 在 excludePathPatterns 中,LoginInterceptor 不拦截
  • LogInterceptor 仍然拦截(它的 addPathPatterns 是 /**,没有排除 /login)

HTTP 请求示例 3:访问上传的文件

$ curl -X GET http://localhost:8080/uploads/avatars/user1.png

响应:

< HTTP/1.1 200
< Content-Type: image/png
<
[二进制图片数据]

流程解析:

  • /uploads/** 匹配 addResourceHandler,Spring 从 file:/var/feixiang/uploads/ 目录查找 avatars/user1.png
  • 不经过任何 Controller 或拦截器(静态资源由 ResourceHttpRequestHandler 处理)

易错场景与面试考点

误区一:@EnableWebMvc 和 WebMvcConfigurer 混用导致自动配置失效

错误代码:

@Configuration
@EnableWebMvc  // 危险!
public class WebConfig implements WebMvcConfigurer {
    // ...
}

错误现象:Spring Boot 的自动配置全部失效,包括静态资源处理、消息转换器、错误页面等。

纠正:在 Spring Boot 项目中,不要加 @EnableWebMvc。Spring Boot 的 WebMvcAutoConfiguration 已经自动配置了 Spring MVC,你只需要实现 WebMvcConfigurer 做增量配置。加 @EnableWebMvc 会关闭自动配置,导致大量默认行为丢失。

传统 Spring MVC 项目(无 Spring Boot)才需要 @EnableWebMvc 来启用注解驱动的 MVC 配置。

误区二:拦截器顺序理解错误

错误认知:"先注册的拦截器先执行 preHandle,也先执行 postHandle 和 afterCompletion。"

纠正:preHandle 是正序执行,postHandle 和 afterCompletion 是逆序执行。这是"责任链"模式的经典设计——先进去的后出来,确保资源清理的嵌套顺序正确。

误区三:excludePathPatterns 使用了错误的匹配规则

错误代码:

registry.addInterceptor(loginInterceptor)
        .addPathPatterns("/**")
        .excludePathPatterns("/api/login");  // 想排除 /api/login

问题:如果请求是 /api/login?redirect=/home,excludePathPatterns 默认只匹配路径,不匹配查询参数,所以能正常排除。但如果写的是:

.excludePathPatterns("/api/*");  // 错误!

/api/login 不会被排除,因为 * 只匹配一级路径,** 才匹配多级路径。

正确写法:

.excludePathPatterns("/api/login", "/api/register", "/static/**");

面试高频:addPathPatterns 和 excludePathPatterns 的优先级

标准回答:excludePathPatterns 优先级高于 addPathPatterns。如果一个 URL 同时匹配两者,排除生效。Spring 内部先检查排除规则,再检查包含规则。

面试高频:Spring Boot 中 WebMvcConfigurer 与 application.properties 配置的关系

标准回答:两者是互补关系:

  • application.properties 适合简单的键值配置(如 server.port、spring.mvc.static-path-pattern)
  • WebMvcConfigurer 适合复杂的编程式配置(如拦截器注册、跨域策略、自定义参数解析器)
  • 如果两者冲突,WebMvcConfigurer 的编程式配置通常优先级更高

小结

WebMvcConfigurer 是 Spring MVC 的配置扩展接口,通过实现该接口的 addInterceptors、addResourceHandlers、addCorsMappings 等方法,开发者可以以声明式方式定制框架行为。在 Spring Boot 项目中,只需实现接口即可(无需 @EnableWebMvc),Spring Boot 会自动收集并应用这些配置。

本章与全局的关系:本章讲解了如何把拦截器注册到 Spring MVC 运行时。下一章"@CrossOrigin"将深入讲解跨域请求的底层机制和配置方式。

上一页
HandlerInterceptor
下一页
CrossOrigin