以下内容是 Spring Framework 官方文档中关于 MVC 配置(MVC Config) 的详细说明,特别是 1.11 节 "MVC Config" 的完整章节。这一部分内容非常系统地介绍了如何通过 Java 注解配置和 XML 配置来定制 Spring MVC 的行为。
以下是对该文档内容的结构化总结与关键点解析,帮助您更好地理解其用途和应用场景:
🌐 Spring MVC 配置概览
Spring 提供了两种方式来启用和自定义 MVC 行为:
- Java 配置 :使用 @EnableWebMvc+ 实现WebMvcConfigurer
- XML 配置 :使用 <mvc:annotation-driven />及相关标签
⚠️ 注意:虽然标题中有 "WebFlux",但本节实际描述的是 Spring MVC(基于 Servlet 的阻塞式框架),而非响应式 WebFlux。可能是文档版本混淆所致。
🔧 各配置项详解
1.11.1 ✅ 启用 MVC 配置
- Java : @EnableWebMvc注解开启默认 MVC 支持。
- XML : <mvc:annotation-driven />实现相同功能。
- 功能:自动注册 RequestMappingHandlerMapping,RequestMappingHandlerAdapter, 消息转换器等核心组件。
1.11.2 🛠️ MVC 配置 API
- 实现 WebMvcConfigurer接口进行扩展(Java)
- XML 中通过 <mvc:annotation-driven>的子标签或属性配置
- 不需要手动创建底层 Bean,Spring 自动处理
1.11.3 🔢 类型转换(Type Conversion)
- 默认注册数字、日期格式化器(支持 @NumberFormat,@DateTimeFormat)
- 自定义方式:
- Java: 重写 addFormatters(FormatterRegistry)
- XML: 定义 FormattingConversionServiceFactoryBean并关联到<mvc:annotation-driven conversion-service="..." />
 
- Java: 重写 
- 特别提示:浏览器对 <input type="date">使用 ISO 格式,建议设置DateTimeFormatterRegistrar.setUseIsoFormat(true)
1.11.4 ✅ 校验(Validation)
- 若类路径存在 Bean Validation(如 Hibernate Validator),自动注册 LocalValidatorFactoryBean
- 自定义全局校验器:
- Java: 重写 getValidator()
- XML: <mvc:annotation-driven validator="globalValidator"/>
 
- Java: 重写 
- 局部校验:在 Controller 中使用 @InitBinder添加特定Validator
1.11.5 🪝 拦截器(Interceptors)
- 
用于请求预处理/后处理 
- 
Java: javaregistry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
- 
XML: xml<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/admin/**"/> <bean class="com.example.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
1.11.6 📦 内容协商(Content Negotiation)
控制如何根据请求决定返回内容类型(JSON/XML 等)
- 
默认策略:先看 URL 扩展名( .json,.xml),再看Accept头
- 
推荐仅使用 Accept头,避免路径污染和安全风险(RFD 攻击)
- 
Java: javaconfigurer.mediaType("json", MediaType.APPLICATION_JSON);
- 
XML: xml<bean class="ContentNegotiationManagerFactoryBean"> <property name="mediaTypes"> json=application/json </property> </bean>
1.11.7 💬 消息转换器(Message Converters)
控制 HTTP 请求/响应体的序列化(如 JSON ↔ 对象)
两种方式:
- configureMessageConverters():替换默认转换器
- extendMessageConverters():扩展或修改默认转换器
✅ 推荐使用
extendMessageConverters()保留默认行为
示例:自定义 Jackson ObjectMapper
            
            
              java
              
              
            
          
          @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
        .indentOutput(true)
        .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
        .modulesToInstall(new ParameterNamesModule());
    converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}Spring Boot 中更推荐用 Jackson2ObjectMapperBuilder 统一配置。
1.11.8 🖼️ 视图控制器(View Controllers)
快捷方式,将某个 URL 直接映射到视图(无需 Controller 方法)
- 
Java: javaregistry.addViewController("/").setViewName("home");
- 
XML: xml<mvc:view-controller path="/" view-name="home"/>
⚠️ 注意:不能与
@RequestMapping冲突,否则会报 405 错误
1.11.9 🧩 视图解析器(View Resolvers)
简化视图技术的配置,如 JSP、Thymeleaf、FreeMarker、JSON 视图等
- 
Java: javaregistry.enableContentNegotiation(new MappingJackson2JsonView()); registry.jsp();
- 
XML: xml<mvc:view-resolvers> <mvc:content-negotiation> <mvc:default-views> <bean class="MappingJackson2JsonView"/> </mvc:default-views> </mvc:content-negotiation> <mvc:jsp/> </mvc:view-resolvers>
若使用 FreeMarker/Tiles 等,还需额外配置模板加载路径(
FreeMarkerConfigurer)
1.11.10 📁 静态资源处理
配置静态文件(CSS/JS/图片)的访问路径和缓存策略
- 
Java: javaregistry.addResourceHandler("/resources/**") .addResourceLocations("/public/", "classpath:/static/") .setCachePeriod(31556926); // 1 year
- 
XML: xml<mvc:resources mapping="/resources/**" location="/public, classpath:/static/" cache-period="31556926"/>
高级功能:资源链(Resource Chain)
支持版本化 URL(防缓存)、Gzip 压缩等
- 使用 VersionResourceResolver+ContentVersionStrategy(基于内容哈希)
- WebJars 支持:自动解析 /jquery/jquery.min.js → /jquery/3.6.0/jquery.min.js
1.11.11 🏠 默认 Servlet 处理
允许将 DispatcherServlet 映射为 /,同时让容器默认 Servlet 处理静态资源
- 
Java: javaconfigurer.enable();
- 
XML: xml<mvc:default-servlet-handler/>
⚠️ 原理:添加一个最低优先级的
DefaultServletHttpRequestHandler,转发未匹配请求给容器默认 Servlet如果默认 Servlet 名称非标准(如 Tomcat 的
default),需手动指定名称
1.11.12 🔍 路径匹配(Path Matching)
自定义 URL 匹配规则
常见配置:
            
            
              java
              
              
            
          
          configurer
    .setUseTrailingSlashMatch(false) // 是否匹配尾部斜杠
    .setUseSuffixPatternMatch(true)  // 是否允许 .json/.xml 后缀匹配
    .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class)); // 为所有 @RestController 添加前缀 /api推荐关闭后缀匹配(
.json)以提升安全性(防止 RFD 攻击)
1.11.13 ⚙️ 高级 Java 配置
- 移除 @EnableWebMvc
- 直接继承 DelegatingWebMvcConfiguration
- 可以 覆盖父类中的 Bean 定义
            
            
              java
              
              
            
          
          @Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
    // 可以 override 方法,修改默认 Bean
}适用于需要深度定制 MVC 内部组件的场景。
1.11.14 📄 高级 XML 配置
XML 模式不支持"高级模式",但可通过 BeanPostProcessor 修改已创建的 Bean:
            
            
              java
              
              
            
          
          @Component
public class MyPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String name) {
        if ("requestMappingHandlerAdapter".equals(name)) {
            // 修改适配器配置
        }
        return bean;
    }
}需配合
<context:component-scan />扫描该处理器
✅ 最佳实践建议
| 配置项 | 推荐做法 | 
|---|---|
| 内容协商 | 禁用路径扩展名匹配,仅使用 Accept头 | 
| 消息转换 | 使用 Jackson2ObjectMapperBuilder统一配置 JSON | 
| 静态资源 | 使用资源链 + 内容哈希版本控制 | 
| 路径匹配 | 关闭 useSuffixPatternMatch和useTrailingSlashMatch | 
| 校验 | 使用 Hibernate Validator + @Valid | 
| 拦截器 | 按路径精细控制,避免影响静态资源 | 
📚 总结
这份文档是 Spring MVC 配置的核心参考手册,涵盖了从基础启用到高级定制的所有方面。它适用于:
- 使用纯 Spring(非 Spring Boot)搭建 Web 应用
- 想要深入了解 Spring MVC 自动配置背后机制的开发者
- 需要脱离"约定优于配置"模式,进行深度定制的项目
💡 在 Spring Boot 中,这些配置大多由
WebMvcAutoConfiguration自动完成,开发者只需通过@Configuration实现WebMvcConfigurer来扩展即可,无需加@EnableWebMvc(否则会关闭自动配置)。
如果您有具体问题,例如:
- "如何在 Spring MVC 中统一返回 JSON 格式?"
- "如何实现静态资源版本控制?"
- "如何添加登录拦截器?"
欢迎继续提问,我可以基于此文档给出具体代码示例!