一句话总结
- 非 Spring Boot 项目:
@EnableWebMvc+WebMvcConfigurer是标准组合。 - Spring Boot 项目:只实现
WebMvcConfigurer就够了,不要加@EnableWebMvc,除非你确实想完全接管 MVC 配置。
@EnableWebMvc注解
@EnableWebMvc 是 Spring Framework 中的一个注解,用于在基于 Java 配置的 Spring Web MVC 应用中启用 Spring MVC 的支持。简单来说,它的作用相当于在传统的 XML 配置文件中使用 <mvc:annotation-driven /> 标签。
@EnableWebMvc 的核心作用
当在一个带有 @Configuration 注解的配置类上添加 @EnableWebMvc 时,Spring 会自动配置好运行一个 Spring MVC 应用所需的基础组件。具体来说,它会:
启用注解驱动的 MVC(即激活对 @Controller、@RequestMapping、@ResponseBody 等注解的支持),使得 HTTP 请求可以被映射到相应的控制器方法上;
提供默认配置:为 Spring MVC 提供一套完整的默认配置,让你能够快速启动和运行一个 Web 应用。
同时会自动注册一系列关键的 Bean,例如:
| 方法 | 用途 |
|---|---|
| RequestMappingHandlerMapping | 用于将请求 URL 映射到处理方法 |
| RequestMappingHandlerAdapter | 用于调用处理请求的方法 |
| HandlerExceptionResolver | 用于处理控制器中抛出的异常 |
| MessageConverter | 用于转换 HTTP 请求和响应体(如,将 JSON 字符串转换为 Java 对象) |
@EnableWebMvc 的工作原理
从该注解的源码 层面来看,@EnableWebMvc 的实现非常简洁,其核心是通过 @Import 注解导入了 DelegatingWebMvcConfiguration 类 。@EnableWebMvc 源码如下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DelegatingWebMvcConfiguration.class) public @interface EnableWebMvc { }- DelegatingWebMvcConfiguration 继承自 WebMvcConfigurationSupport,而后者是 Spring MVC 配置的核心支持类,负责创建和配置上述提到的所有核心组件;
- 同时,DelegatingWebMvcConfiguration 还会收集所有实现了 WebMvcConfigurer 接口的 Bean,并将它们的配置应用到 Spring MVC 的总配置中,这使得开发者可以方便地覆盖或扩展默认配置。
在SpringBoot框架 中的应用与注意事项
在传统的 Spring MVC 项目(非 Spring Boot项目)中,@EnableWebMvc 是标准配置,用于开启 MVC 功能。而在 Spring Boot 项目中,情况则完全不同:
Spring Boot 的 spring-boot-starter-web 依赖会通过 WebMvcAutoConfiguration 类自动配置 Spring MVC 。这个自动配置会根据你的依赖和配置文件(如 application.properties)来智能地创建 Bean,并提供了很多便利的默认行为,例如:
- 自动配置静态资源路径(如 /static, /public, /resources, /META-INF/resources);
- 自动配置视图解析器;
- 允许通过 application.properties 中的 spring.mvc.* 前缀来配置 MVC;
- …
但在 Spring Boot 项目中,99% 的场景都不需要 @EnableWebMvc。 因为如果在 Spring Boot 项目 中添加 @EnableWebMvc 注解,实际上是完全接管了 Spring MVC 的配置。因为:
- Spring Boot 的自动配置类 WebMvcAutoConfiguration 上有个条件注解 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)。这意味着,一旦容器中存在 WebMvcConfigurationSupport 的实例(由 @EnableWebMvc 导入),Spring Boot 的一些自动配置就会失效。只有当 Spring 容器中不存在 WebMvcConfigurationSupport 类型的 Bean 时,WebMvcAutoConfiguration 才会生效。
- 而@EnableWebMvc 注解通过 @Import 导入 DelegatingWebMvcConfiguration 这个配置类。DelegatingWebMvcConfiguration 继承自 WebMvcConfigurationSupport,因此它本身就是一个 WebMvcConfigurationSupport 类型的Bean。因此,当 DelegatingWebMvcConfiguration 被注册到容器后,WebMvcAutoConfiguration 就会随之失效。
此时 SpringBoot 项目使用 @EnableWebMvc 带来的后果:
- 静态资源访问失效。 无法再通过默认的路径(如 http://localhost:8080/logo.png)访问 static 目录下的静态文件。你需要手动配置资源映射;
- 配置文件属性失效。在 application.properties 或 application.yml 中配置的 spring.mvc 相关属性(如视图前缀/后缀)将不再生效;
- 完全手动配置。此时需要我们自己负责所有 MVC 组件的配置,包括视图解析器、消息转换器、拦截器等。
需用@EnableWebMvc的场景总结
也就是说使用了 @EnableWebMvc 注解后,必须自己重新实现所有 Spring Boot 原本帮你做好的事情,这样极易出错且增加维护成本。因此在 Spring Boot 项目中,大多数情况下不需要使用 @EnableWebMvc。需要使用@EnableWebMvc的场景如下:
- 完全不需要 Spring Boot 的 Web 自动配置时;
- 希望从零开始完全控制 Spring MVC 的每一个细节时;
- 在开发一个非 Spring Boot 项目(纯 Spring MVC)时。
WebMvcConfigurer接口
WebMvcConfigurer接口是 Spring Framework( Spring Web MVC 模块)中一个非常重要的接口,用于自定义 Spring MVC 的配置,而无需提供完整的@EnableWebMvc配置类。它在 Spring Boot 应用中尤其常见。
WebMvcConfigurer接口提供的方法
WebMvcConfigurer提供了一系列默认方法,允许开发者选择性地重写需要定制的行为,部分常用方法如下:
| 方法 | 用途 |
|---|---|
addInterceptors(InterceptorRegistry) | 注册拦截器 |
addCorsMappings(C CorsRegistry) | 配置跨域请求 |
addViewControllers(ViewControllerRegistry) | 配置无逻辑的页面跳转(如/about → forward:/about.html) |
configurePathMatch(PathMatchConfigurer) | 配置 URL 路径匹配规则(如是否启用后缀模式匹配) |
extendMessageConverters(List<HttpMessageConverter<?>>) | 扩展或替换默认的消息转换器(如 JSON、XML) |
addFormatters(FormatterRegistry) | 添加自定义的类型转换器或格式化器 |
addResourceHandlers(ResourceHandlerRegistry) | 配置静态资源(如 CSS、JS、图片)的访问路径 |
注意不要同时使用 @EnableWebMvc 和 WebMvcConfigurer,除非明确要完全接管 Spring MVC 的自动配置(在 Spring Boot 中会禁用默认的自动配置)。在 Spring Boot 中,推荐仅实现 WebMvcConfigurer 接口而不加 @EnableWebMvc,这样可以保留 Spring Boot 的自动配置(如默认的静态资源处理、欢迎页、错误页等),同时又能灵活扩展。
原理:当我们创建了一个带有 @Configuration 注解的类,并实现了WebMvcConfigurer接口。此时:
- @Configuration 注解告诉 Spring 容器:“这是一个配置类,请把我管理起来。” Spring 启动时进行组件扫描,会把这个类识别出来并注册为一个 Bean;
- Spring Boot 的 MVC 自动配置核心类 WebMvcAutoConfiguration 内部,有一个名为 EnableWebMvcConfiguration 的类 。这个配置类继承自 DelegatingWebMvcConfiguration。(DelegatingWebMvcConfiguration继承自WebMvcConfigurationSupport);
- DelegatingWebMvcConfiguration 的关键作用在于,它会利用依赖注入,自动查找 Spring 容器中所有实现了 WebMvcConfigurer 接口的 Bean;
- DelegatingWebMvcConfiguration 会遍历它收集到的所有 WebMvcConfigurer 实例,并依次调用它们被重写的方法。这意味着,我们添加的自定义配置和 Spring Boot 默认的 MVC 配置会被合并在一起,共同生效;
总结:在Spring Boot中自定义 MVC 配置
在绝大多数情况下,如果我们只是想对 Spring Boot 的 MVC 配置进行一些微调(例如添加拦截器、配置消息转换器、增加视图控制器等),就不应该使用 @EnableWebMvc。正确的做法是:创建一个配置类,实现 WebMvcConfigurer 接口。通过实现 WebMvcConfigurer 接口,可以只重写我们关心的配置方法,而不会影响 Spring Boot 的自动配置。这样既能实现自定义,又能保留 Spring Boot 带来的便利。
在非springboot工程中
@EnableWebMvc 是“开关”,WebMvcConfigurer 是“定制接口”,两者常搭配使用,但作用完全不同。
- @EnableWebMvc:开启 Spring MVC 的注解驱动配置,相当于 XML 里的
<mvc:annotation-driven />。它通过@Import(DelegatingWebMvcConfiguration.class)导入一个WebMvcConfigurationSupport的子类,由这个子类去创建 MVC 的核心组件(如RequestMappingHandlerMapping、消息转换器等)。 - WebMvcConfigurer:一个回调接口,里面全是
default方法,用来定制 MVC 的配置,比如加拦截器、配跨域、自定义静态资源映射、配置视图解析器等。它本身不“开启”任何功能,只是提供扩展点
在非 Spring Boot 的纯 Spring MVC 项目里,通常这样用:
@Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { //配置jsp页面位置 @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp("/WEB-INF/views/",".jsp"); } /** * 开启静态资源查找 * dispatcherServlet 通知 handlerMapping根据静态资源路径找对应的handler 若没找到 * 如果不开启静态资源查找,请求返回给dispatcherServlet后直接再返回给前端,404异常。 * 如果开启了静态资源查找, 就会再调用 defaultServletHandler查找静态资源(拿着静态资源路径做了一次转发。) */ @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }@EnableWebMvc导入的DelegatingWebMvcConfiguration会自动收集容器里所有WebMvcConfigurer的 Bean,并把它们的配置合并到 MVC 全局配置中。
在 Spring Boot 里要特别注意
Spring Boot 已经通过WebMvcAutoConfiguration自动配置好了 MVC,默认情况下不需要加@EnableWebMvc,只需实现WebMvcConfigurer做增量定制即可。
一旦加了@EnableWebMvc,它会导入DelegatingWebMvcConfiguration(它是WebMvcConfigurationSupport的子类),而WebMvcAutoConfiguration上有@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),条件不满足,Spring Boot 的 MVC 自动配置会整个失效——静态资源映射、默认消息转换器、spring.mvc配置项等全部丢失,需要手动补齐。