Spring配置接口WebMvcConfigurer
一、概述
WebMvcConfigurer是SpringMVC提供的一个配置回调接口,它定义了一系列用于自定义SpringMVC行为的方法,只需创建一个配置类实现该接口,并按需重写方法,即可轻松实现URL映射、拦截器注册、跨域配置等功能,无需编写繁琐的XML配置。
关键特性:
- 无侵入性:仅需实现接口并重写需要的方法,不影响其他默认配置;
- 丰富的配置项:覆盖
URL映射、拦截器、静态资源、视图解析器、跨域等核心场景; - 与
SpringBoot自动配置兼容:无需额外添加@EnableWebMvc注解(添加后会覆盖SpringBoot默认配置,需谨慎使用)。
二、接口源码
WebMvcConfigurer位于org.springframework.web.servlet.config.annotation包下,是一个基于Java8的接口,其中大部分方法都是default类型的,且都是空实现。因此只需要定义一个配置类实现WebMvcConfigurer接口,并重写相应的方法便可以定制SpringMVC的配置。
public interface WebMvcConfigurer {
//HandlerMappings路径的匹配规则。
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) {}
//添加 Spring MVC 生命周期拦截器,对请求进行拦截处理。
default void addInterceptors(InterceptorRegistry registry) {}
//添加或修改静态资源(例如图片,js,css等)映射;
//Spring Boot 默认设置的静态资源文件夹就是通过重写该方法设置的。
default void addResourceHandlers(ResourceHandlerRegistry registry) {}
//处理跨域请求。
default void addCorsMappings(CorsRegistry registry) {}
//主要用于实现无业务逻辑跳转,例如主页跳转,简单的请求重定向,错误页跳转等
default void addViewControllers(ViewControllerRegistry registry) {}
//配置视图解析器,将Controller返回的字符串(视图名称),转换为具体的视图进行渲染。
default void configureViewResolvers(ViewResolverRegistry registry) {}
//添加解析器以支持自定义控制器方法参数类型,实现该方法不会覆盖用于解析处理程序方法参数的内置支持;
//要自定义内置的参数解析支持,同样可以通过RequestMappingHandlerAdapter直接配置RequestMappingHandlerAdapter。
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}
//添加处理程序来支持自定义控制器方法返回值类型。使用此选项不会覆盖处理返回值的内置支持;
//要自定义处理返回值的内置支持,请直接配置RequestMappingHandlerAdapter。
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}
//用于配置默认的消息转换器(转换HTTP请求和响应)。
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
//直接添加消息转换器,会关闭默认的消息转换器列表;
//实现该方法即可在不关闭默认转换器的起提下,新增一个自定义转换器。
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}
//配置异常解析器。
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
//扩展或修改默认的异常解析器列表。
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
}
在SpringBoot项目中,可以通过以下2种形式定制SpringMVC:
- 扩展
SpringMVC:如果SpringBoot对SpringMVC的自动配置不能满足当前的需要,可以通过自定义一个WebMvcConfigurer类型(实现WebMvcConfigurer接口)的配置类(标注@Configuration,但不标注@EnableWebMvc注解的类),来扩展SpringMVC。这样不但能够保留SpringBoot对SpringMVC的自动配置,享受SpringBoot自动配置带来的便利,还能额外增加自定义的SpringMVC配置。 - 全面接管
SpringMVC:在一些特殊情况下,可能需要抛弃SpringBoot对SpringMVC的全部自动配置,完全接管SpringMVC。此时可以自定义一个WebMvcConfigurer类型(实现WebMvcConfigurer接口)的配置类,并在该类上标注@EnableWebMvc注解,来实现完全接管SpringMVC。
注意:完全接管
SpringMVC后,SpringBoot对SpringMVC的自动配置将全部失效。
三、底层原理
要彻底掌握WebMvcConfigurer的使用,需理解其底层设计逻辑和Spring加载配置的核心流程。本节从核心类关系、加载流程、关键源码拆解三个维度,还原WebMvcConfigurer的生效机制。
3.1 非侵入式配置
SpringMVC的核心配置逻辑封装在WebMvcConfigurationSupport类中(MVC配置的“底层骨架”),而WebMvcConfigurer是Spring为开发者预留的配置扩展接口——它采用「回调模式」设计,开发者通过实现接口定义自定义规则,Spring初始化MVC容器时会自动回调这些规则,将自定义配置合并到默认配置中。
3.1.1 核心类关系
| 类/接口 | 核心定位 | 关键作用 |
|---|---|---|
| WebMvcConfigurer | 配置扩展接口 | 定义URL映射、拦截器、静态资源等配置方法,无默认实现,仅作为扩展点 |
| WebMvcConfigurerComposite | 配置组合类 | 聚合容器中所有WebMvcConfigurer实现类,统一回调所有自定义配置方法 |
| WebMvcConfigurationSupport | MVC配置基类 | 实现SpringMVC核心组件(HandlerMapping、HandlerAdapter、ViewResolver)的创建逻辑 |
| DelegatingWebMvcConfiguration | 配置代理类 | 继承WebMvcConfigurationSupport,注入WebMvcConfigurerComposite,将自定义配置融入核心配置 |
| WebMvcAutoConfiguration | SpringBoot自动配置类 | 未加@EnableWebMvc时生效,自动加载默认MVC配置,并聚合自定义WebMvcConfigurer |
3.1.2 优势
- 非侵入性:无需修改
SpringMVC核心代码,仅通过实现接口即可扩展; - 灵活性:按需重写方法,无需覆盖全部配置;
- 兼容性:与
SpringBoot“约定大于配置”理念兼容,默认配置与自定义配置可共存。
3.2 加载流程
SpringBoot启动时,WebMvcConfigurer的配置会经历「自动配置触发→配置类扫描→配置聚合→核心组件初始化」四个阶段,以下结合源码拆解关键步骤:
步骤1:自动配置触发
SpringBoot启动时,WebMvcAutoConfiguration(MVC自动配置类)会被@EnableAutoConfiguration扫描并加载,其核心条件注解决定了默认配置的生效规则:
// WebMvcAutoConfiguration核心源码(简化版)
@AutoConfiguration(
after = {DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class}
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
// 关键条件:容器中不存在WebMvcConfigurationSupport时生效(即未加@EnableWebMvc)
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebMvcAutoConfiguration {
// 注入容器中所有自定义的WebMvcConfigurer实现类
@Autowired(required = false)
private List<WebMvcConfigurer> mvcConfigurers = Collections.emptyList();
// 创建WebMvcConfigurerComposite,聚合所有自定义配置
@Bean
public WebMvcConfigurerComposite mvcConfigurerComposite() {
WebMvcConfigurerComposite composite = new WebMvcConfigurerComposite();
composite.addMvcConfigurers(this.mvcConfigurers);
return composite;
}
// 内部配置类:将自定义配置融入默认配置
@Configuration(proxyBeanMethods = false)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
private final WebMvcConfigurerComposite configurers;
// 注入聚合后的配置类
public WebMvcAutoConfigurationAdapter(WebMvcConfigurerComposite configurers) {
this.configurers = configurers;
}
// 回调自定义配置方法(如addInterceptors、addResourceHandlers)
@Override
public void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
}
}
关键说明:
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class):若容器中存在WebMvcConfigurationSupport(如加了@EnableWebMvc),则WebMvcAutoConfiguration失效,默认配置被覆盖;mvcConfigurers:自动注入所有加了@Configuration的WebMvcConfigurer实现类(开发者自定义的配置类);WebMvcConfigurerComposite:将多个自定义配置类聚合,避免多配置类冲突。
步骤2:配置类扫描与聚合
WebMvcConfigurerComposite是Spring处理多WebMvcConfigurer的核心,其核心逻辑是聚合所有配置类,并统一回调方法:
// WebMvcConfigurerComposite核心源码
public class WebMvcConfigurerComposite implements WebMvcConfigurer {
private final List<WebMvcConfigurer> delegates = new ArrayList<>();
// 添加所有自定义WebMvcConfigurer
public void addMvcConfigurers(@Nullable List<WebMvcConfigurer> configurers) {
if (configurers != null) {
this.delegates.addAll(configurers);
}
}
// 回调所有自定义配置类的addInterceptors方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
// 回调所有自定义配置类的addResourceHandlers方法
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addResourceHandlers(registry);
}
}
// 其他方法(addViewControllers、addCorsMappings等)同理
}
关键说明:
- 多配置类场景下,
Spring会遍历所有WebMvcConfigurer实现类,依次执行其重写方法; - 可通过
@Order注解指定配置类优先级,优先级高的配置类先执行(如@Order(1)比@Order(2)先执行)。
步骤3:核心组件初始化
若添加了@EnableWebMvc,WebMvcAutoConfiguration失效,此时DelegatingWebMvcConfiguration会接管配置流程:
// @EnableWebMvc核心源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 导入DelegatingWebMvcConfiguration,覆盖默认配置
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {}
// DelegatingWebMvcConfiguration核心源码
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
// 注入所有自定义 WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addMvcConfigurers(configurers);
}
}
// 重写WebMvcConfigurationSupport的方法,融入自定义配置
@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
}
关键说明:
@EnableWebMvc会导入DelegatingWebMvcConfiguration,而DelegatingWebMvcConfiguration继承WebMvcConfigurationSupport;- 由于
WebMvcAutoConfiguration的@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)条件,此时自动配置类失效,SpringBoot默认的静态资源路径、视图解析器等配置全部被覆盖。
步骤4:自定义配置生效
无论是否加@EnableWebMvc,Spring最终都会通过WebMvcConfigurationSupport创建核心组件,并将自定义配置融入其中:
- 拦截器:通过
InterceptorRegistry注册的拦截器,最终被添加到RequestMappingHandlerMapping(处理@RequestMapping的核心HandlerMapping); - 静态资源:
ResourceHandlerRegistry的配置会生成ResourceHandlerMapping,处理静态资源请求; - 视图解析器:
ViewResolverRegistry的配置会合并到ViewResolver列表,优先级由order属性决定。
3.3 源码落地
以最常用的addInterceptors为例,拆解自定义拦截器如何被Spring加载并生效:
第一步:开发者自定义配置类
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
}
}
第二步:WebMvcConfigurerComposite聚合配置
Spring将WebMvcConfig注入WebMvcConfigurerComposite的delegates列表,调用addInterceptors时遍历执行:
// WebMvcConfigurerComposite的addInterceptors方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry); // 执行开发者自定义的addInterceptors
}
}
第三步:WebMvcConfigurationSupport初始化拦截器
// WebMvcConfigurationSupport核心源码
protected final Object[] getInterceptors() {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
// 回调addInterceptors方法,加载自定义拦截器
addInterceptors(registry);
// 添加Spring内置拦截器(如ConversionServiceExposingInterceptor)
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
// 封装拦截器为HandlerInterceptor对象
this.interceptors = registry.getInterceptors().toArray();
}
return this.interceptors;
}
// 将拦截器注册到RequestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
// 设置拦截器
mapping.setInterceptors(getInterceptors());
// 其他配置...
return mapping;
}
第四步:拦截器生效
DispatcherServlet处理请求时,会通过RequestMappingHandlerMapping获取拦截器链,依次执行preHandle、postHandle、afterCompletion方法。
四、核心方法实战
下面通过“配置类+代码示例”的形式,讲解开发中最常用的6个核心方法。
4.1 视图映射
当需要将某个URL直接映射到指定视图(无需通过Controller)时,可使用addViewControllers方法,简化无业务逻辑的页面跳转配置。
示例:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration // 标记为配置类,Spring会自动扫描并加载
public class WebMvcConfig implements WebMvcConfigurer {
// 配置URL与视图的直接映射
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 访问"/home"直接跳转到"home.html"视图
registry.addViewController("/home").setViewName("home");
// 访问"/login"直接跳转到"login.html",并设置HTTP状态码为200
registry.addViewController("/login").setViewName("login").setStatus(200);
// 访问根路径"/"跳转到"index.html"
registry.addViewController("/").setViewName("index");
}
}
场景:适合登录页、首页、404页面等无业务逻辑的静态页面跳转,避免创建空的Controller。
4.2 自定义拦截器
拦截器是SpringMVC中实现“请求前置处理、后置处理”的核心组件(如登录校验、日志记录)。通过addInterceptors方法可注册自定义拦截器,并指定拦截/排除的URL规则。
步骤1:自定义拦截器
先创建一个实现HandlerInterceptor接口的拦截器类:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
// 登录校验拦截器
public class LoginInterceptor implements HandlerInterceptor {
// 请求处理前执行(如校验登录状态)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 从Session中获取用户信息
Object user = request.getSession().getAttribute("loginUser");
if (user == null) {
// 未登录,重定向到登录页
response.sendRedirect("/login");
return false; // 拦截请求,不继续向下执行
}
// 已登录,放行请求
return true;
}
}
步骤2:注册拦截器
在WebMvcConfig中重写addInterceptors方法,注册拦截器并配置规则:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()) // 注册自定义拦截器
.addPathPatterns("/**") // 拦截所有URL
.excludePathPatterns( // 排除不需要拦截的URL
"/login", "/register", // 登录、注册页
"/static/**", // 静态资源(CSS/JS/图片)
"/error" // 错误页
);
}
}
注意:
addPathPatterns:指定需要拦截的URL(支持通配符,如/**表示所有路径);excludePathPatterns:指定无需拦截的URL(避免拦截静态资源或公开页面)。
4.3 静态资源访问规则
SpringBoot默认将classpath:/static/、classpath:/public/等目录作为静态资源根目录,但实际开发中可能需要自定义静态资源路径(如映射到外部磁盘目录),此时可通过addResourceHandlers实现。
示例1:自定义类路径下的静态资源映射
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 访问"/assets/**"映射到"classpath:/my-static/"目录
registry.addResourceHandler("/assets/**")
.addResourceLocations("classpath:/my-static/")
// 缓存控制(可选):设置静态资源缓存时间为3600秒
.setCachePeriod(3600);
}
示例2:映射外部磁盘目录的静态资源
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 访问"/upload/**"映射到"D:/file-upload/"本地磁盘目录
registry.addResourceHandler("/upload/**")
.addResourceLocations("file:D:/file-upload/"); // 注意前缀"file:"
}
场景:适合文件上传后预览(如图片、文档),或需要将静态资源放在外部磁盘的场景。
4.4 跨域请求
前后端分离项目中,前端(如Vue、React)与后端(SpringBoot)通常运行在不同端口,会遇到跨域问题。通过addCorsMappings可快速配置跨域规则,允许指定域名的请求访问后端接口。
示例:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // 对"/api"前缀的所有接口生效
.allowedOrigins("http://localhost:8080") // 允许前端域名(如Vue项目运行地址)
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的HTTP方法
.allowedHeaders("*") // 允许的请求头(如Token)
.allowCredentials(true) // 是否允许携带Cookie
.maxAge(3600); // 预检请求(OPTIONS)的缓存时间(秒)
}
说明:
allowedOrigins("*"):允许所有域名跨域(生产环境不推荐,需指定具体域名);allowCredentials(true):若需要前端携带Cookie,需设置为true,且allowedOrigins不能为*。
4.5 视图解析器
视图解析器用于将Controller返回的“视图名”解析为实际的视图文件(如JSP、HTML、Thymeleaf模板)。通过configureViewResolvers可自定义视图解析器的前缀、后缀或优先级。
示例:配置Thymeleaf视图解析器
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// 配置JSP视图解析器(若使用JSP)
InternalResourceViewResolver jspResolver = new InternalResourceViewResolver();
jspResolver.setPrefix("/WEB-INF/views/"); // 视图文件前缀
jspResolver.setSuffix(".jsp"); // 视图文件后缀
jspResolver.setViewClass(JstlView.class);
jspResolver.setOrder(1); // 优先级(数字越小优先级越高)
// 注册视图解析器
registry.viewResolver(jspResolver);
// 若使用Thymeleaf,SpringBoot会自动配置,无需额外代码
// 若需自定义Thymeleaf前缀,可通过application.properties配置:
// spring.thymeleaf.prefix=classpath:/templates/my-pages/
}
场景:适合使用JSP作为视图模板的项目,或需要多视图解析器共存的场景。
4.6 默认Servlet处理
当请求无法被SpringMVC的DispatcherServlet处理时(如静态资源),可通过configureDefaultServletHandling将请求交给容器默认的Servlet处理,避免404错误。
示例:
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// 启用默认Servlet处理:未被Spring MVC处理的请求交给容器默认Servlet
configurer.enable();
}
注意:该方法通常与
addResourceHandlers配合使用,确保静态资源能被正确访问。
五、注意事项
- 避免滥用@EnableWebMvc注解
若在配置类上添加@EnableWebMvc,会完全覆盖SpringBoot的默认MVC配置(如默认静态资源路径、Thymeleaf自动配置),导致自动配置失效。仅当需要完全自定义MVC配置时才使用。
- 配置类需加@Configuration注解
必须在实现WebMvcConfigurer的类上添加@Configuration注解,否则Spring无法识别该配置类。
- 多配置类的优先级
若存在多个实现WebMvcConfigurer的配置类,可通过@Order注解指定优先级(如@Order(1)比@Order(2)优先级高),优先级高的配置会先生效。
- 拦截器与跨域的顺序问题
跨域预检请求(OPTIONS)会先于拦截器执行,若拦截器中未排除OPTIONS请求,可能导致跨域配置失效。需在addInterceptors中通过excludePathPatterns排除OPTIONS请求。
六、总结
WebMvcConfigurer是SpringMVC中灵活且强大的配置工具,通过实现该接口并按需重写方法,可轻松完成URL映射、拦截器、静态资源、跨域等核心配置,替代传统的XML配置方式。其核心优势在于“按需配置、无侵入性、与SpringBoot自动配置兼容”,是前后端分离、传统MVC项目开发中的必备技能。
如果在使用过程中遇到配置不生效、拦截器冲突等问题,可优先检查:配置类是否加@Configuration、是否误加@EnableWebMvc、URL规则是否正确(通配符是否匹配)。
七、常见问题
问题1:为什么加了@EnableWebMvc后静态资源访问失效?
答:@EnableWebMvc导入DelegatingWebMvcConfiguration(继承WebMvcConfigurationSupport),导致WebMvcAutoConfiguration失效——而SpringBoot默认的静态资源配置(如classpath:/static/)是在WebMvcAutoConfiguration中定义的,因此默认静态资源路径失效,需手动通过addResourceHandlers配置。
问题2:多WebMvcConfigurer配置类的执行顺序?
答:Spring注入List
问题3:为什么拦截器排除OPTIONS请求才能解决跨域问题?
答:跨域预检请求(OPTIONS)由CorsFilter处理,而拦截器执行优先级高于CorsFilter——若拦截器未排除OPTIONS请求,会在预检请求阶段拦截请求,导致CorsFilter无法生效,跨域配置失效。

浙公网安备 33010602011771号