全面接管 SpringMVC 的思路

SpringBoot 默认配置好了 SpringMVC 的所有常⽤特性。

在 SpringBoot 中,全面接管 SpringMVC 配置的思路是通过禁用默认配置并完全自定义所有 MVC 行为。这是一个高级用法,适用于需要对 SpringMVC 进行深度定制的场景。

全面接管 SpringMVC 的核心步骤如下:

  1. 创建一个配置类,实现WebMvcConfigurer接口
  2. 使用@EnableWebMvc注解禁用 SpringBoot 的默认 MVC 配置
  3. 重写WebMvcConfigurer接口中的方法来定义所有需要的 MVC 行为,WebMvcConfigurer组件定义MVC的底层行为

回忆一下配置类的三种自定义方式

全自动 直接编写控制器逻辑 全部使用自动配置默认效果
手自一体 @Configuration + 配置 WebMvcConfigurer + 配置 WebMvcRegistrations 不要标注@EnableWebMvc 自动配置效果 手动设置部分功能 定义 MVC 底层组件
全手动 @Configuration + 配置 WebMvcConfigurer 标注@EnableWebMvc 禁用自动配置效果 全手动设置

例如,如下就是一个全面接管 SpringMVC 配置的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.CacheControl;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableWebMvc // 禁用SpringBoot默认的MVC配置
public class WebMvcConfig implements WebMvcConfigurer {

// 配置静态资源处理
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}

// 配置视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
registry.addViewController("/hello").setViewName("hello");
registry.addViewController("/login").setViewName("login");
}

// 配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}

// 配置跨域支持
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("GET", "POST")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
}

// 配置路径匹配
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false)
.setUseTrailingSlashMatch(true);
}

// 配置内容协商
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(org.springframework.http.MediaType.APPLICATION_JSON);
}

// 配置消息转换器
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 自定义消息转换器配置
// 注意:这里需要完全自定义,SpringBoot不会添加默认的转换器
}

// 配置异常处理器
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
// 自定义异常处理器
}

// 配置参数解析器
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// 添加自定义参数解析器
}

// 配置返回值处理器
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
// 添加自定义返回值处理器
}

// 配置数据格式化
@Override
public void addFormatters(FormatterRegistry registry) {
// 添加自定义格式化器
}

// 配置Validator
@Override
public Validator getValidator() {
// 返回自定义Validator
return null;
}

// 配置消息代码解析器
@Override
public MessageCodesResolver getMessageCodesResolver() {
// 返回自定义消息代码解析器
return null;
}

// 配置RequestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors());
// 自定义配置
return mapping;
}
}
  1. @EnableWebMvc 注解:这个注解是全面接管的关键,它会禁用 SpringBoot 的自动 MVC 配置,让你完全控制所有 MVC 行为。
  2. WebMvcConfigurer 接口:实现这个接口并重写其方法,可以自定义各种 MVC 行为:
    • 静态资源处理
    • 视图控制器配置
    • 拦截器注册
    • 跨域支持
    • 路径匹配策略
    • 内容协商
    • 消息转换器
    • 异常处理器
    • 参数解析器
    • 返回值处理器
    • 数据格式化
  3. 自定义 Bean:除了实现接口方法外,还可以通过 @Bean注解提供自定义的组件,如 RequestMappingHandlerMappingValidator等。

WebMvcConfigurer 接口分析及方法详解

源码分析

org.springframework.web.servlet.config.annotation.WebMvcConfigurer 是 Spring MVC 提供的一个接口,用于自定义 Web MVC 的配置。它是一个 标记接口(marker interface),它本身没有任何抽象方法,但提供了一组默认为空实现的方法(Java 8+ 的 default 方法),开发者可以选择性地覆盖这些方法来定制 Spring MVC 的行为。

来看看WebMvcConfigurer接口的源代码,对应方法我编写了注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package org.springframework.web.servlet.config.annotation;

import java.util.List;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.ErrorResponse;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;

/**
* 用于自定义Spring MVC配置的接口。
* 实现此接口的类可以选择性地重写其方法来自定义Spring MVC的行为,
* 而无需使用XML配置或完全替换默认配置。
*
* 注意:如果使用@EnableWebMvc注解,Spring Boot的自动配置将被禁用,
* 所有MVC配置都必须通过实现此接口来显式定义。
*/
public interface WebMvcConfigurer {

/**
* 配置路径匹配选项,如后缀模式匹配、尾部斜杠匹配等。
*/
default void configurePathMatch(PathMatchConfigurer configurer) {
}

/**
* 配置内容协商策略,用于确定请求和响应的媒体类型。
*/
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}

/**
* 配置异步请求处理选项,如异步请求超时时间、任务执行器等。
*/
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}

/**
* 配置默认Servlet处理,用于处理静态资源请求。
*/
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}

/**
* 注册自定义的格式化器和转换器,用于类型转换和格式化。
*/
default void addFormatters(FormatterRegistry registry) {
}

/**
* 注册自定义的拦截器,用于处理请求的预处理和后处理。
*/
default void addInterceptors(InterceptorRegistry registry) {
}

/**
* 注册自定义的资源处理器,用于处理静态资源请求。
*/
default void addResourceHandlers(ResourceHandlerRegistry registry) {
}

/**
* 配置跨域资源共享(CORS)映射,允许跨域请求。
*/
default void addCorsMappings(CorsRegistry registry) {
}

/**
* 注册视图控制器,用于直接映射URL到视图而不需要控制器方法。
*/
default void addViewControllers(ViewControllerRegistry registry) {
}

/**
* 配置视图解析器,用于将逻辑视图名称解析为实际的视图实现。
*/
default void configureViewResolvers(ViewResolverRegistry registry) {
}

/**
* 注册自定义的处理器方法参数解析器,用于处理控制器方法的参数。
*/
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}

/**
* 注册自定义的处理器方法返回值处理器,用于处理控制器方法的返回值。
*/
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}

/**
* 配置HTTP消息转换器列表。此方法用于完全自定义消息转换器,
* 会替换默认的转换器列表。
*/
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}

/**
* 扩展默认的HTTP消息转换器列表。此方法用于在默认转换器的基础上添加自定义转换器,
* 而不是替换它们。
*/
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}

/**
* 配置处理器异常解析器列表。此方法用于完全自定义异常解析器,
* 会替换默认的解析器列表。
*/
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}

/**
* 扩展默认的处理器异常解析器列表。此方法用于在默认解析器的基础上添加自定义解析器,
* 而不是替换它们。
*/
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}

/**
* 添加错误响应拦截器,用于自定义错误响应的处理。
*/
default void addErrorResponseInterceptors(List<ErrorResponse.Interceptor> interceptors) {
}

/**
* 提供自定义的Validator实现,用于数据验证。
*/
@Nullable
default Validator getValidator() {
return null;
}

/**
* 提供自定义的MessageCodesResolver实现,用于解析验证错误代码。
*/
@Nullable
default MessageCodesResolver getMessageCodesResolver() {
return null;
}
}

总结下来就是:

方法名 描述 常用场景
configurePathMatch 配置路径匹配选项,如后缀模式匹配、尾部斜杠匹配等 自定义 URL 匹配规则,如禁用.html后缀匹配
configureContentNegotiation 配置内容协商策略,用于确定请求和响应的媒体类型 设置默认响应格式为 JSON,配置基于 URL 扩展名的内容协商
configureAsyncSupport 配置异步请求处理选项,如异步请求超时时间、任务执行器等 设置异步请求超时时间,配置 DeferredResult 处理
configureDefaultServletHandling 配置默认 Servlet 处理,用于处理静态资源请求 启用默认 Servlet 处理静态资源
addFormatters 注册自定义的格式化器和转换器,用于类型转换和格式化 添加日期格式化器,注册自定义类型转换器
addInterceptors 注册自定义的拦截器,用于处理请求的预处理和后处理 添加身份验证拦截器,实现请求日志记录
addResourceHandlers 注册自定义的资源处理器,用于处理静态资源请求 配置静态资源路径,设置缓存控制头
addCorsMappings 配置跨域资源共享 (CORS) 映射,允许跨域请求 允许特定域名访问 API,配置跨域请求头
addViewControllers 注册视图控制器,用于直接映射 URL 到视图而不需要控制器方法 简化登录页面、主页等简单视图的映射
configureViewResolvers 配置视图解析器,用于将逻辑视图名称解析为实际的视图实现 配置 JSP、Thymeleaf 等视图解析器
addArgumentResolvers 注册自定义的处理器方法参数解析器,用于处理控制器方法的参数 添加自定义参数解析器,如用户会话参数
addReturnValueHandlers 注册自定义的处理器方法返回值处理器,用于处理控制器方法的返回值 添加自定义返回值处理器,如统一 API 响应格式
configureMessageConverters 配置 HTTP 消息转换器列表,会替换默认转换器 完全自定义消息转换器,如使用自定义 JSON 序列化器
extendMessageConverters 扩展默认的 HTTP 消息转换器列表 在默认转换器基础上添加自定义转换器
configureHandlerExceptionResolvers 配置处理器异常解析器列表,会替换默认解析器 完全自定义异常处理机制
extendHandlerExceptionResolvers 扩展默认的处理器异常解析器列表 添加自定义异常处理器
addErrorResponseInterceptors 添加错误响应拦截器,用于自定义错误响应的处理 自定义错误响应格式,添加额外错误信息
getValidator 提供自定义的 Validator 实现,用于数据验证 提供自定义验证器,如使用 Hibernate Validator
getMessageCodesResolver 提供自定义的 MessageCodesResolver 实现,用于解析验证错误代码 自定义错误代码解析逻辑

接下来我会挑出其中重要的进行分析和讲解

configureMessageConverters vs extendMessageConverters

Spring MVC 默认提供了一系列消息转换器,如 MappingJackson2HttpMessageConverter(JSON)、StringHttpMessageConverter

这两个方法都用于配置 HTTP 消息转换器,但作用不同

  • configureMessageConverters: 完全自定义消息转换器列表,会替换默认的转换器
  • extendMessageConverters: 在默认转换器的基础上添加自定义转换器
1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 完全自定义消息转换器,不使用默认转换器
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new StringHttpMessageConverter());
}

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 在默认转换器的基础上添加自定义转换器
converters.add(new MyCustomHttpMessageConverter());
}

建议使用 extendMessageConvertersextendHandlerExceptionResolvers 而不是完全替换默认配置

底层实现差异:

1
2
3
4
5
6
7
8
// 在RequestMappingHandlerAdapter初始化时调用
if (configurers.configureMessageConverters) {
// 完全替换默认转换器
adapter.setMessageConverters(converters);
} else {
// 追加自定义转换器
adapter.addMessageConverters(converters);
}

addInterceptors(InterceptorRegistry registry)

用于注册自定义拦截器,可以在请求处理前后执行特定逻辑,如身份验证、日志记录等。

拦截器很像 Servlet 中的过滤器(Filter),但拦截器提供了更精细的控制和更丰富的功能,允许你在请求处理的不同阶段执行特定逻辑

拦截器是实现横切关注点的好方法,如认证、日志、性能监控等,在这里写一些登录常用的逻辑很不错

1
2
3
4
5
6
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthenticationInterceptor())
.addPathPatterns("/api/**") // 拦截路径
.excludePathPatterns("/api/public/**"); // 排除路径
}

拦截器链的执行原理

sequenceDiagram
    participant DispatcherServlet
    participant Interceptor1
    participant Interceptor2
    participant Handler
    
    DispatcherServlet->>Interceptor1: preHandle()
    Interceptor1->>Interceptor2: preHandle()
    Interceptor2->>Handler: 执行控制器方法
    Handler-->>Interceptor2: postHandle()
    Interceptor2-->>Interceptor1: postHandle()
    Interceptor1-->>DispatcherServlet: afterCompletion()

拦截器与过滤器对比

特性 拦截器(Interceptor) 过滤器(Filter)
作用域 Spring MVC上下文 Servlet容器层面
依赖 依赖Spring框架 仅需Servlet API
执行时机 控制器方法前后 请求进入Servlet之前
异常处理 可接入ControllerAdvice 只能跳转错误页面

addResourceHandlers

用于配置静态资源处理器,如 CSS、JavaScript、图片等文件的访问路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 自定义静态资源路径
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));

// 配置WebJars支持
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");

// 配置自定义欢迎页
registry.addResourceHandler("/")
.addResourceLocations("classpath:/public/index.html")
.setCacheControl(CacheControl.noCache());
}

addCorsMappings

用于配置跨域资源共享 (CORS),允许浏览器从不同域名访问 API。

CORS 原理

  • 浏览器实施同源策略,跨域请求需服务器明确允许
  • 简单请求直接发送,预检请求(复杂请求)先发送 OPTIONS 请求
  • Spring MVC 通过设置响应头(如Access-Control-Allow-Origin)实现 CORS 支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://example.com", "https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("Content-Type", "Authorization", "X-Requested-With")
.exposedHeaders("Location", "X-Total-Count")
.allowCredentials(true)
.maxAge(3600); // 预检请求缓存1小时

// 针对特定API的细粒度配置
registry.addMapping("/api/auth/**")
.allowedOrigins("*")
.allowedMethods("POST", "OPTIONS")
.maxAge(3600);
}

configureHandlerExceptionResolvers vs extendHandlerExceptionResolvers

默认异常解析器

  1. ExceptionHandlerExceptionResolver - 处理带@ExceptionHandler注解的方法
  2. ResponseStatusExceptionResolver - 处理带@ResponseStatus注解的异常
  3. DefaultHandlerExceptionResolver - 处理 Spring MVC 标准异常

这两个方法用于配置异常处理器:

  • configureHandlerExceptionResolvers: 完全自定义异常处理器列表
  • extendHandlerExceptionResolvers: 在默认处理器的基础上添加自定义处理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
// 添加自定义异常解析器
resolvers.add(new MyCustomExceptionResolver());

// 找到并修改默认的ExceptionHandlerExceptionResolver
resolvers.stream()
.filter(resolver -> resolver instanceof ExceptionHandlerExceptionResolver)
.findFirst()
.ifPresent(resolver -> {
ExceptionHandlerExceptionResolver exResolver = (ExceptionHandlerExceptionResolver) resolver;
// 自定义配置
});
}

configureContentNegotiation(ContentNegotiationConfigurer configurer)

配置内容协商策略,根据请求头或 URL 参数决定返回的数据格式(如 JSON、XML)。

1
2
3
4
5
6
7
8
9
10
11
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.favorParameter(true) // 优先使用请求参数
.parameterName("format") // 参数名
.ignoreAcceptHeader(false) // 不忽略Accept头
.useRegisteredExtensionsOnly(false) // 允许未注册的扩展名
.defaultContentType(MediaType.APPLICATION_JSON) // 默认内容类型
.mediaType("json", MediaType.APPLICATION_JSON) // 注册媒体类型映射
.mediaType("xml", MediaType.APPLICATION_XML);
}

configureAsyncSupport(AsyncSupportConfigurer configurer)

异步请求类型

  1. Callable - 控制器返回Callable,在单独线程中执行
  2. DeferredResult - 控制器返回DeferredResult,在另一个线程中设置结果
  3. ResponseBodyEmitter - 流式响应
  4. SseEmitter - 服务器发送事件
  5. StreamingResponseBody - 流式响应体

配置异步请求支持,例如设置任务执行超时时间、线程池等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
// 设置超时时间
configurer.setDefaultTimeout(30000); // 30秒

// 配置异步任务执行器
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsyncTask-");
executor.initialize();
configurer.setTaskExecutor(executor);

// 注册异步请求拦截器
configurer.registerCallableInterceptors(new TimeoutCallableProcessingInterceptor());
configurer.registerDeferredResultInterceptors(new TimeoutDeferredResultProcessingInterceptor());
}

configurePathMatch(PathMatchConfigurer configurer)

关键配置项

  • setUseSuffixPatternMatch - 是否启用后缀模式匹配(如/users.html匹配/users
  • setUseTrailingSlashMatch - 是否启用尾部斜杠匹配(如/users/匹配/users
  • setUseRegisteredSuffixPatternMatch - 仅匹配已注册的后缀
  • setPathMatcher - 设置自定义路径匹配器
  • setUrlPathHelper - 设置自定义 URL 路径帮助器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 禁用后缀模式匹配(防止/resource.json绕过安全检查)
configurer.setUseSuffixPatternMatch(false);

// 启用尾部斜杠匹配
configurer.setUseTrailingSlashMatch(true);

// 配置路径匹配器
AntPathMatcher pathMatcher = new AntPathMatcher();
pathMatcher.setCaseSensitive(true); // 区分大小写
pathMatcher.setTrimTokens(true); // 去除路径中的空白字符
configurer.setPathMatcher(pathMatcher);
}

WebMvcAutoConfiguration 到底自动配置了哪些规则

SpringMVC 自动配置场景给我们配置了如下所有默认行为,其中的过程和涉及到的细节内容我也一起讲解,因为东西太多,只是过一遍会很混乱

  1. WebMvcAutoConfiguration web 场景的自动配置类

    • 支持 RESTful 的 filter:HiddenHttpMethodFilter

      • 作用:在浏览器表单仅支持 GETPOST 原生请求方法的限制下,通过请求参数(默认参数名为 _method ),将 POST 请求转换为 PUTDELETEPATCH 等符合 RESTful 规范的请求方法,让普通表单也能适配 RESTful 风格接口。
      • 例如:前端页面要发送一个删除资源的 DELETE 请求,但浏览器无法直接发起,就可以用 POST 表单携带 _method=DELETE 参数,经此 Filter 转换后,后端控制器就能用 @DeleteMapping 接收处理。
    • 支持非 POST 请求,请求体携带数据:FormContentFilter

      • 作用:对于 PUTPATCH 等非 POST 请求,解决浏览器默认不会像 POST 请求那样自动携带表单数据(请求体内容 )的问题。它会解析请求体中的表单参数,并将这些参数填充到请求对象中,使后端控制器方法能像处理 POST 请求参数一样,正常获取 PUTPATCH 等请求携带的表单数据。比如前端用 PUT 请求更新用户信息,携带表单形式的用户姓名、年龄等参数,后端通过 @RequestParam 等注解就能顺利拿到这些参数进行业务处理。
      • 生效逻辑:自动注册到 Filter 链,针对 PUTPATCH 等方法的请求,在请求处理前期解析请求体表单数据,为后续控制器方法执行做准备。
    • 导入 EnableWebMvcConfiguration

      EnableWebMvcConfigurationWebMvcAutoConfiguration 内部类,继承自 DelegatingWebMvcConfiguration ,它借助 Spring 框架的配置机制,进一步初始化和配置一系列 Spring MVC 核心组件,具体如下:

      • RequestMappingHandlerAdapter:

        • 作为 Spring MVC 中处理请求的重要适配器,负责调用被 @RequestMapping(及衍生的 @GetMapping@PostMapping 等 )标注的控制器方法。在调用过程中,它会处理请求参数的绑定(比如将 HTTP 请求参数转换为控制器方法的入参类型 )、返回值的处理(把控制器方法返回结果转换为合适的响应数据格式,如 JSON、视图名等 ),是连接请求、控制器方法、响应的关键桥梁。
        • 初始化:由 EnableWebMvcConfiguration 依据 Spring 容器的 Bean 管理机制,结合默认配置和用户可能的自定义配置(若有实现 WebMvcConfigurer 相关参数处理定制 ),完成创建和初始化,确保能适配各种常见的参数、返回值处理场景。
      • WelcomePageHandlerMapping:欢迎页功能支持(模板引擎目录、静态资源目录放 index.html)

        • 当访问应用根路径(/ )时,它会按照一定优先级顺序去查找欢迎页资源。优先在模板引擎可解析的目录(如使用 Thymeleaf 时的 classpath:/templates/ )下查找 index.html ,若未找到,会到静态资源目录(classpath:/META-INF/resources/classpath:/resources/classpath:/static/classpath:/public/ 等 )中寻找 index.html 文件,找到后就会将该页面作为欢迎页展示,让访问根路径的请求能直接呈现友好的首页内容。
      • RequestMappingHandlerMapping:找每个请求由谁处理的映射关系

        • 负责扫描 Spring 容器中所有被 @Controller@RestController 标注的控制器类,以及这些类中被 @RequestMapping 及其衍生注解(@GetMapping@PostMapping 等 )标注的方法,然后建立并维护 “请求路径 ↔︎ 控制器方法” 的映射关系。后续当有 HTTP 请求到达时,能依据请求路径快速找到对应的控制器方法进行处理,是 Spring MVC 实现请求路由的核心基础。
        • 扫描与构建:在应用启动时,它会遍历符合条件的 Bean ,解析注解信息,构建起庞大的请求路径映射注册表,并且在应用运行过程中,若有 Bean 动态加载(如通过 Bean 定义注册器新增控制器 Bean ),也能适时更新映射关系。
      • ExceptionHandlerExceptionResolver:默认的异常解析器

        • 作为 Spring MVC 默认的异常解析器,用于处理控制器方法执行过程中抛出的异常。它会查找被 @ExceptionHandler 标注的异常处理方法(可以是当前控制器类中的方法,也能是通过 @ControllerAdvice 全局配置的方法 ),将异常交由对应的处理方法进行逻辑处理(比如返回自定义错误提示、错误码等 ),最终生成合适的响应(可以是 JSON 数据、错误页面等 )返回给客户端,保证应用遇到异常时能有统一、友好的错误处理表现。
        • 异常处理流程:当控制器方法抛出异常后,DispatcherServlet 会将异常交给 ExceptionHandlerExceptionResolver ,它依据异常类型去匹配对应的 @ExceptionHandler 方法,执行方法得到处理结果后,再转换为响应发送给客户端,若未找到匹配的处理方法,会按默认的异常处理逻辑(如返回 500 错误等 )处理。
      • LocaleResolver:国际化解析器

        • 用于处理 Web 应用的国际化(多语言 )需求,解析请求中的语言区域信息。默认情况下,它会根据 HTTP 请求头中的 Accept-Language 字段来判断客户端期望的语言类型(如 zh-CN 表示中文 - 中国,en-US 表示英文 - 美国 ),然后结合应用中配置的国际化资源文件(如 messages_zh_CN.propertiesmessages_en_US.properties 等 ),为后续的视图渲染、消息提示等提供对应的语言环境支持,让应用能根据不同地区用户展示合适的语言内容。
        • 若默认的基于 Accept-Language 的解析方式不能满足需求(比如想通过请求参数 ?lang=zh 来切换语言 ),可通过实现 LocaleResolver 接口自定义解析逻辑,再配置到 Spring MVC 环境中替换默认实现。
      • ThemeResolver:主题解析器

        • 主要用于处理 Web 应用的主题切换需求,比如不同的页面风格、样式主题等。它会依据一定的规则(默认可通过请求参数、Session 等方式 )确定当前请求对应的主题,后续视图解析、资源加载等环节就能根据主题信息,使用对应的主题资源(如不同主题的 CSS 样式文件、页面模板等 ),实现应用界面主题的灵活切换和展示。
        • 应用场景:在一些需要让用户自定义界面风格(如浅色主题、深色主题 )的系统中,通过 ThemeResolver 配合主题相关的视图解析、资源管理,能很好地实现不同主题的切换和呈现。
      • FlashMapManager:临时数据共享

        • 实现重定向时临时数据的共享功能。当控制器方法处理完请求,需要重定向到另一个 URL ,并且希望在重定向后能获取到一些 “一次性” 的数据(如提示信息 “操作成功,请查看详情” )时,可通过 FlashMap 存储这些数据,FlashMapManager 负责管理 FlashMap 的创建、存储(通常存储在 Session 中 )和获取。重定向前将数据存入 FlashMap ,重定向后的请求处理时,FlashMapManager 会取出数据供后续使用,使用完后这些数据会被清除,保证是 “一次性” 的。

          1
          2
          3
          4
          5
          6
          7
          @PostMapping("/save")
          public String saveData(RedirectAttributes redirectAttributes) {
          // 业务处理...
          redirectAttributes.addFlashAttribute("msg", "数据保存成功");
          return "redirect:/list";
          // 在重定向后的 /list 对应的控制器方法或视图中,就能获取到 msg 这个一次性提示数据。
          }
      • FormattingConversionService:数据格式化、类型转化

        • 职责:承担着数据格式化和类型转换的重要职责。在请求参数绑定阶段,它会将 HTTP 请求中的字符串参数,按照一定规则转换为控制器方法入参的对应类型(比如把请求参数中的字符串 “2024-10-01” 转换为 java.util.Date 类型 );在响应数据处理时,也能将一些复杂类型(如自定义的枚举类型 )格式化为合适的字符串或其他可传输的格式。同时,它默认集成了对 JSR303 校验相关的支持,在数据转换、绑定过程中,能配合校验注解(如 @DateTimeFormat 结合 @Valid )进行数据格式的校验和转换。
        • 扩展能力:用户可通过实现 FormatterConverter 接口,自定义数据格式化和类型转换规则,然后注册到 FormattingConversionService 中,实现对特殊类型数据(如自定义的日期格式、自定义业务枚举 )的个性化转换和格式化。
      • Validator:数据校验 JSR303 提供的数据校验功能

        • 职责:提供数据校验功能,基于 JSR303(Java Bean Validation 规范 )及后续的 JSR349、JSR380 等规范,支持使用 @Valid@NotBlank@Size 等校验注解,对控制器方法的入参(如 @RequestBody 接收的对象、@RequestParam 绑定的参数等 )、实体类的属性进行校验。当校验不通过时,会抛出对应的校验异常,可结合 ExceptionHandlerExceptionResolver 进行统一的异常处理和错误提示返回,保证输入数据的合法性和规范性。
      • WebBindingInitializer:请求参数的封装与绑定

        • 在 Spring MVC 请求参数绑定的过程中,负责初始化数据绑定的相关设置。它会参与到请求参数转换为控制器方法入参的整个流程,比如设置默认的类型转换器(关联到 FormattingConversionService )、初始化数据绑定的一些基础规则(如是否允许空值、日期格式的默认处理等 ),确保请求参数能准确、合理地绑定到控制器方法的参数上,为控制器方法正确执行提供数据基础。
        • 关联关系:与 FormattingConversionService 紧密协作,FormattingConversionService 提供具体的类型转换和格式化能力,WebBindingInitializer 则在绑定流程中运用这些能力,同时进行一些初始化、规则设置的工作。
      • ContentNegotiationManager:内容协商管理器

        • 职责:实现内容协商功能,决定客户端请求期望的响应数据格式,以及服务端实际返回的响应数据格式。它会综合考虑多个因素来确定响应格式,包括请求头中的 Accept 字段(客户端期望接收的媒体类型,如 application/jsontext/html 等 )、请求参数(可通过设置参数如 ?format=json 来指定响应格式 )等。根据协商结果,Spring MVC 会选择合适的消息转换器(如 MappingJackson2HttpMessageConverter 用于转换 JSON 格式 )来处理响应数据,保证返回给客户端的数据格式是其期望或可接受的。
        • 应用场景:在一个同时提供 JSON 数据接口和 HTML 页面展示的 Web 应用中,通过 ContentNegotiationManager ,就能根据客户端不同的请求(如浏览器直接访问返回 HTML 页面,手机端接口调用返回 JSON 数据 ),灵活返回对应的响应格式,提升应用的兼容性和适用性。
    • WebMvcAutoConfigurationAdapter配置生效,它是一个WebMvcConfigurer,定义 mvc 底层组件

      WebMvcAutoConfigurationAdapterWebMvcAutoConfiguration 内部的静态类,它实现了 WebMvcConfigurer 接口,作为 Spring Boot 自动配置的具体实现,用来定义 Spring MVC 底层组件的默认配置,以下是其关键配置点:

      • 定义好WebMvcConfigurer 底层组件默认功能;所有功能详见下面或者上面的列表

      • 视图解析器:InternalResourceViewResolver

        • 主要用于解析 JSP 等 “内部资源视图”。当控制器方法返回一个视图名(如 return "userInfo" )时,它会按照配置的前缀(如 prefix = "/WEB-INF/views/" )和后缀(如 suffix = ".jsp" )规则,拼接出完整的视图资源路径(即 /WEB-INF/views/userInfo.jsp ),然后找到对应的 JSP 文件进行渲染,将动态数据填充到 JSP 页面中,最终返回给客户端呈现。在 WebMvcAutoConfigurationAdapter 中,会依据 Spring Boot 的默认配置策略,设置合适的前缀和后缀(也可通过 application.properties 等配置文件进行自定义调整 ),适配常见的 JSP 视图存储目录结构,让基于 JSP 的 Web 应用能便捷地进行视图解析和渲染。
      • 视图解析器: BeanNameViewResolver , 视图名(controller 方法的返回值字符串)就是组件名

        • 将控制器方法返回的视图名,直接作为 Spring 容器中 View 类型 Bean 的名称去查找对应的视图组件。也就是说,若控制器方法返回视图名 “customView” ,那么 Spring 容器中需要存在一个名为 “customView” 且类型为 View 的 Bean(可以是自定义的 View 实现类 ),BeanNameViewResolver 会找到该 Bean 并使用它来渲染视图,实现灵活的视图定制。
      • 内容协商解析器: ContentNegotiatingViewResolver

        • 它本身并不直接解析视图,而是结合 ContentNegotiationManager 的内容协商结果,从多个视图解析器(如 InternalResourceViewResolverBeanNameViewResolver 等 )中选择合适的视图解析器来解析视图。例如,内容协商确定需要返回 HTML 格式的视图,它就会在能解析 HTML 视图的解析器中挑选(如 InternalResourceViewResolver 若配置了解析 JSP 生成 HTML ),然后使用该解析器进行视图解析和渲染,是一个 “协调者” 角色,整合多种视图解析器和内容协商功能。
        • 工作流程:先通过 ContentNegotiationManager 确定响应数据格式需求,再遍历其管理的视图解析器列表,找到第一个能满足该格式需求且能解析对应视图名的解析器,交由其进行视图解析和渲染,保证在内容协商的基础上,能正确找到并使用合适的视图资源。
      • 请求上下文过滤器: RequestContextFilter:任意位置直接获取当前请求

        • 为应用中任意位置(包括非 Web 层的 Service、Component 等 )提供获取当前 HTTP 请求上下文(HttpServletRequestHttpServletResponse )的能力。它会在请求处理过程中,将当前的请求和响应对象绑定到线程局部变量(ThreadLocal )中,后续在同一线程的任意位置,都可以通过 RequestContextHolder.currentRequestAttributes() 等方式获取到当前请求的上下文信息,方便一些非 Web 层组件在需要时使用请求相关的数据(如获取请求头信息、Session 数据等 )。
        • 注意事项:因为是基于 ThreadLocal 实现,所以要注意线程安全和资源释放问题,在异步处理场景中,若不正确处理,可能会导致获取到错误的请求上下文或资源泄漏,不过 Spring Boot 对常见的异步场景也有相应的处理机制来保障其正确性。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        public class aService {
        public void getRequest(){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        // 任意位置通过 RequestContextHolder 获取到当前请求和响应的信息
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();

        String url = request.getRequestURI();
        }
        }
      • 静态资源链规则

      • ProblemDetailsExceptionHandler:错误详情

        • 统一处理 Spring MVC 内部抛出的异常,生成标准化的错误详情响应。当控制器方法抛出异常(如参数校验失败的 MethodArgumentNotValidException 、资源未找到的 NoSuchRequestHandlingMethodException 等 )时,它会将异常信息封装成包含 type(错误类型标识 )、title(错误标题 )、status(HTTP 状态码 )、detail(错误详情 )等字段的 JSON 响应,让客户端能更清晰、规范地识别错误。
      • 定义了 MVC 默认的底层行为:WebMvcConfigurer

        • WebMvcAutoConfigurationAdapter 本身就是 WebMvcConfigurer 的实现类,它为 Spring MVC 的所有底层行为提供了 “开箱即用” 的默认配置

核心总结

WebMvcAutoConfiguration 作为 Spring Boot 自动配置的核心,通过以下方式为 Web 应用搭建 MVC 基石:

  1. Filter 层:通过 HiddenHttpMethodFilterFormContentFilter 补全浏览器请求能力,适配 RESTful 风格和非 POST 请求的表单处理。
  2. 核心组件层:借助 EnableWebMvcConfiguration 初始化 RequestMappingHandlerMappingExceptionHandlerExceptionResolver 等 MVC 核心组件,构建 “请求路由 → 处理 → 异常处理” 的完整流程。
  3. 默认配置层:通过 WebMvcAutoConfigurationAdapter 实现 WebMvcConfigurer ,为视图解析、资源处理、错误详情等提供开箱即用的默认规则,同时预留 WebMvcConfigurer 扩展点,支持用户按需覆盖。

理解这些后,再看 Spring Boot 中的 Web 应用:大部分 MVC 需求(如静态资源、视图解析、异常处理 )都能 “零配置” 运行,这背后正是 WebMvcAutoConfiguration 的功劳;而当需要定制(如修改 JSON 序列化规则、添加自定义拦截器 )时,只需实现 WebMvcConfigurer ,就能在默认配置基础上 “微创” 调整,既享受自动配置的便捷,又保留定制化的灵活性 。

使用表格总结如下

方法名 默认规则 核心参数 常用场景
configurePathMatch 默认启用后缀模式匹配(如/users.html匹配/users)和尾部斜杠匹配(如/users/匹配/users - setUseSuffixPatternMatch: 是否启用后缀模式匹配 - setUseTrailingSlashMatch: 是否启用尾部斜杠匹配 - setPathMatcher: 设置自定义路径匹配器 自定义 URL 匹配规则,如禁用.html后缀匹配
configureContentNegotiation 默认基于请求的 Accept 头确定响应内容类型,支持 JSON、XML 等 - defaultContentType: 设置默认内容类型 - favorPathExtension: 是否优先使用 URL 扩展名进行内容协商 - mediaTypes: 注册媒体类型映射 设置默认响应格式为 JSON,配置基于 URL 扩展名的内容协商
configureAsyncSupport 默认异步请求超时时间为 30 秒,使用 SimpleAsyncTaskExecutor 执行异步任务 - setDefaultTimeout: 设置异步请求超时时间 - setTaskExecutor: 设置异步任务执行器 - registerCallableInterceptors: 注册 Callable 拦截器 设置异步请求超时时间,配置 DeferredResult 处理
configureDefaultServletHandling 默认不启用默认 Servlet 处理,静态资源由 Spring MVC 处理 - enable: 启用默认 Servlet 处理 - enableDispatcherTypes: 指定 DispatcherType 启用默认 Servlet 处理静态资源
addFormatters 默认注册 JSR-310 日期时间格式化器、数字格式化器等 - FormatterRegistry: 用于注册格式化器和转换器的注册表 添加日期格式化器,注册自定义类型转换器
addInterceptors 默认不注册任何拦截器 - InterceptorRegistry: 用于注册拦截器的注册表 - addPathPatterns: 指定拦截路径 - excludePathPatterns: 指定排除路径 - order: 设置拦截器顺序 添加身份验证拦截器,实现请求日志记录
addResourceHandlers 默认从classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/加载静态资源,缓存控制头为 365 天 - addResourceHandler: 指定 URL 路径 - addResourceLocations: 指定资源位置 - setCacheControl: 设置缓存控制头 - setCachePeriod: 设置缓存时间(秒) 配置静态资源路径,设置缓存控制头
addCorsMappings 默认不配置任何 CORS 映射 - addMapping: 指定 CORS 映射路径 - allowedOrigins: 允许的源 - allowedMethods: 允许的 HTTP 方法 - allowedHeaders: 允许的请求头 - allowCredentials: 是否允许发送 Cookie - maxAge: 预检请求的有效期(秒) 允许特定域名访问 API,配置跨域请求头
addViewControllers 默认不注册任何视图控制器 - ViewControllerRegistry: 用于注册视图控制器的注册表 - addViewController: 添加视图控制器 - setViewName: 设置视图名称 简化登录页面、主页等简单视图的映射
configureViewResolvers 默认根据依赖自动配置视图解析器,如 Thymeleaf、FreeMarker 等 - ViewResolverRegistry: 用于配置视图解析器的注册表 - jsp: 配置 JSP 视图解析器 - thymeleaf: 配置 Thymeleaf 视图解析器 - freemarker: 配置 FreeMarker 视图解析器 配置 JSP、Thymeleaf 等视图解析器
addArgumentResolvers 默认注册 RequestParamMethodArgumentResolver、PathVariableMethodArgumentResolver 等 - HandlerMethodArgumentResolver: 自定义参数解析器列表 添加自定义参数解析器,如用户会话参数
addReturnValueHandlers 默认注册 RequestResponseBodyMethodProcessor、ViewNameMethodReturnValueHandler 等 - HandlerMethodReturnValueHandler: 自定义返回值处理器列表 添加自定义返回值处理器,如统一 API 响应格式
configureMessageConverters 默认注册 MappingJackson2HttpMessageConverter、StringHttpMessageConverter 等 - HttpMessageConverter: 自定义消息转换器列表(将替换默认转换器) 完全自定义消息转换器,如使用自定义 JSON 序列化器
extendMessageConverters 默认注册 MappingJackson2HttpMessageConverter、StringHttpMessageConverter 等 - HttpMessageConverter: 自定义消息转换器列表(添加到默认转换器之后) 在默认转换器基础上添加自定义转换器
configureHandlerExceptionResolvers 默认注册 ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver 等 - HandlerExceptionResolver: 自定义异常解析器列表(将替换默认解析器) 完全自定义异常处理机制
extendHandlerExceptionResolvers 默认注册 ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver 等 - HandlerExceptionResolver: 自定义异常解析器列表(添加到默认解析器之后) 添加自定义异常处理器
addErrorResponseInterceptors 默认不注册任何错误响应拦截器 - ErrorResponse.Interceptor: 错误响应拦截器列表 自定义错误响应格式,添加额外错误信息
getValidator 默认使用 Spring 的 LocalValidatorFactoryBean,支持 JSR-303/349 验证 - 返回实现 Validator 接口的自定义验证器 提供自定义验证器,如使用 Hibernate Validator
getMessageCodesResolver 默认使用 DefaultMessageCodesResolver - 返回实现 MessageCodesResolver 接口的自定义解析器 自定义错误代码解析逻辑

@EnableWebMvc 禁用默行为的原理

@EnableWebMvc 的作用

  • 核心逻辑:

    @EnableWebMvc 会向 Spring 容器中导入 DelegatingWebMvcConfiguration组件。

    • DelegatingWebMvcConfigurationWebMvcConfigurationSupport 的子类 ,这意味着一旦使用 @EnableWebMvc,本质上就是让 WebMvcConfigurationSupport 相关逻辑进入容器。

Spring Boot 中 Spring MVC 自动配置的条件

  • 关键注解WebMvcAutoConfiguration(Spring Boot 对 Spring MVC 的自动配置类)上有一个条件注解:

    1
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

    含义是:当 Spring 容器中没有 WebMvcConfigurationSupport 类型的 Bean 时WebMvcAutoConfiguration 才会生效,进而启用 Spring Boot 对 Spring MVC 的默认自动配置。

@EnableWebMvc 禁用默认自动配置的原因

  • 因为 @EnableWebMvc 导入了 WebMvcConfigurationSupport(通过 DelegatingWebMvcConfiguration 间接引入 ),此时容器中存在 WebMvcConfigurationSupport 类型的 Bean。
  • 这就触发了 WebMvcAutoConfiguration 的条件注解 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 不满足条件,导致 WebMvcAutoConfiguration 失效,最终禁用了 Spring Boot 对 Spring MVC 的默认自动配置行为。

所以,@EnableWebMvc 的影响体现在,一旦在配置类上标注 @EnableWebMvc,会让 Spring Boot 中 Spring MVC 的自动配置失效,需要你自己通过 WebMvcConfigurer(或直接继承 WebMvcConfigurationSupport)手动配置 Spring MVC 的底层组件(如拦截器、消息转换器、视图解析器等 。

WebMvcConfigurer 的角色就充当是一个定义 Spring MVC 底层组件功能的接口,你可以实现它来定制 Spring MVC 行为。即使不用 @EnableWebMvc(即保留 Spring Boot 自动配置),也能通过 WebMvcConfigurer 做定制化扩展;但用了 @EnableWebMvc 后,自动配置失效,更需要实现 WebMvcConfigurer 接口或更底层的 WebMvcConfigurationSupport 接口来手动搭建 MVC 功能,实现自己的 WebMvcConfigurer配置类。