0%

springboot扩展SpringMVC

Spring Boot 扩展 Spring MVC 详解:保留自动配置与完全自定义配置指南

Spring Boot 对 Spring MVC 提供了完善的自动配置(如默认的 DispatcherServlet、视图解析器、静态资源映射),可满足大部分场景需求。但实际开发中,常需自定义 MVC 特性(如添加拦截器、调整静态资源路径、自定义消息转换器)。从 “自动配置基础→扩展配置(保留自动配置)→完全自定义(抛弃自动配置)→底层原理” 四个维度,系统讲解 Spring Boot 扩展 Spring MVC 的实现方式与底层逻辑,帮你灵活控制 MVC 配置。

Spring Boot 对 Spring MVC 的自动配置回顾

在学习 “扩展” 前,需先明确 Spring Boot 为 Spring MVC 提供的默认自动配置(核心由 WebMvcAutoConfiguration 类实现),避免重复配置或配置冲突:

自动配置项 核心作用 默认行为示例
DispatcherServlet Spring MVC 核心控制器 自动注册,映射路径为 /(所有请求)
静态资源映射 处理 CSS、JS、图片等静态资源 映射 /static/**/public/**classpath:/static/ 等目录
视图解析器 解析逻辑视图名到物理视图路径 支持 Thymeleaf(默认)、JSP 等,前缀 classpath:/templates/,后缀 .html
消息转换器 处理请求体 / 响应体与 Java 对象的转换 默认支持 JSON(MappingJackson2HttpMessageConverter)、表单格式等
拦截器自动注册 注册 Spring 内置拦截器(如路径匹配拦截器) 无自定义拦截器,需手动扩展

扩展 Spring MVC:保留自动配置(推荐)

大部分场景下,无需完全替换自动配置,只需在其基础上补充自定义逻辑(如添加拦截器、调整静态资源)。Spring Boot 提供 WebMvcConfigurer 接口实现这一需求,且 Spring Boot 2.x+ 推荐直接实现该接口(替代过时的 WebMvcConfigurerAdapter)。

1. 核心接口:WebMvcConfigurer(Spring Boot 2.x+ 推荐)

WebMvcConfigurer 是 Spring MVC 提供的 “全局配置接口”,包含一系列 default 方法(Java 8+ 特性),开发者可按需重写,无需实现所有方法。常用方法及作用如下:

方法名 核心作用 对应 Spring MVC 原生配置(XML)
addInterceptors 添加自定义拦截器(如登录拦截、日志拦截) <mvc:interceptors> 标签
addResourceHandlers 配置静态资源映射(如自定义路径映射) <mvc:resources mapping="" location=""/>
configureViewResolvers 配置视图解析器(如调整前缀、后缀) <bean class="InternalResourceViewResolver">
configureMessageConverters 配置消息转换器(如自定义 JSON 解析器) <mvc:message-converters> 标签
addFormatters 添加类型转换器(如日期格式化、数字格式化) <mvc:formatters> 标签
addViewControllers 快速配置视图控制器(无需编写 Controller) <mvc:view-controller path="" view-name=""/>

2. 实战:实现 WebMvcConfigurer 扩展配置

场景 1:添加登录拦截器(拦截未登录请求)
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
// 1. 自定义拦截器(实现 HandlerInterceptor)
public class LoginInterceptor implements HandlerInterceptor {
// 请求处理前执行(判断是否登录)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从 Session 获取登录用户
User loginUser = (User) request.getSession().getAttribute("loginUser");
if (loginUser == null) {
// 未登录,重定向到登录页
response.sendRedirect("/login");
return false; // 阻止请求继续执行
}
return true; // 已登录,允许请求继续
}
}

// 2. 配置类:实现 WebMvcConfigurer,注册拦截器
@Configuration // 声明为配置类(关键:无需 @EnableWebMvc)
public class MyMvcConfig implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截所有请求,排除登录页、登录接口、静态资源
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 拦截所有路径
.excludePathPatterns(
"/login", // 登录页
"/user/login", // 登录接口
"/static/**", // 静态资源(CSS、JS)
"/templates/**" // 模板文件(Thymeleaf)
);
}
}
场景 2:自定义静态资源映射

默认静态资源映射为 /static/**classpath:/static/,若需将 /my-static/** 映射到 classpath:/my-resources/,可重写 addResourceHandlers

1
2
3
4
5
6
7
8
9
10
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 自定义静态资源映射:/my-static/** → classpath:/my-resources/
registry.addResourceHandler("/my-static/**") // 访问路径
.addResourceLocations("classpath:/my-resources/") // 实际文件路径
.setCachePeriod(3600); // 缓存时间(秒),优化性能
}
}

效果:访问 http://localhost:8080/my-static/css/style.css,实际读取 resources/my-resources/css/style.css 文件。

场景 3:调整视图解析器(Thymeleaf 前缀 / 后缀)

默认 Thymeleaf 视图解析器配置为:前缀=classpath:/templates/后缀=.html。若需调整为 前缀=classpath:/views/,可重写 configureViewResolvers

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// 配置 Thymeleaf 视图解析器
registry.thymeleaf()
.prefix("classpath:/views/") // 自定义前缀
.suffix(".html") // 保留后缀
.templateMode("HTML5") // 模板模式
.encoding("UTF-8"); // 编码
}
}

效果:Controller 返回 return "user/list" 时,实际解析 resources/views/user/list.html

3. 底层原理:为什么实现 WebMvcConfigurer 能保留自动配置?

Spring Boot 的 WebMvcAutoConfiguration(MVC 自动配置类)内部有一个关键内部类 WebMvcAutoConfigurationAdapter,它会收集所有 WebMvcConfigurer 类型的 Bean,并合并其配置到自动配置中:

关键源码拆解:
  1. WebMvcAutoConfigurationAdapter 类WebMvcAutoConfiguration 内部):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Configuration
    @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    // 注入所有 WebMvcConfigurer 类型的 Bean
    private final List<WebMvcConfigurer> configurers;

    // 构造函数:Spring 自动注入所有 WebMvcConfigurer Bean
    public WebMvcAutoConfigurationAdapter(
    WebMvcProperties mvcProperties,
    ResourceProperties resourceProperties,
    List<WebMvcConfigurer> configurers) {
    this.configurers = configurers;
    }

    // 初始化时,合并所有 WebMvcConfigurer 的配置
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    // 先执行自动配置的拦截器,再执行自定义 WebMvcConfigurer 的拦截器
    for (WebMvcConfigurer configurer : this.configurers) {
    configurer.addInterceptors(registry);
    }
    }
    // 其他方法(如 addResourceHandlers)同理,均会合并自定义配置
    }
  2. 核心逻辑

    • 自定义的 MyMvcConfig(实现 WebMvcConfigurer)会被 Spring 扫描为 Bean;
    • WebMvcAutoConfigurationAdapter 注入所有 WebMvcConfigurer Bean,合并其配置(如拦截器、静态资源映射);
    • 最终效果:自动配置的基础功能保留,自定义配置叠加生效(不冲突的配置项共存,冲突项自定义覆盖自动配置)。

完全自定义:抛弃 Spring Boot 自动配置

若需完全掌控 Spring MVC 配置(如自定义 DispatcherServlet 映射、重写所有拦截器逻辑),可通过 添加 @EnableWebMvc 注解 抛弃 Spring Boot 的自动配置,完全基于原生 Spring MVC 规则配置。

1. 实现方式:@EnableWebMvc + 实现 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
@Configuration
@EnableWebMvc // 关键:添加此注解,抛弃自动配置
public class FullCustomMvcConfig implements WebMvcConfigurer {
// 重写所有需要的方法(无默认配置,需手动实现所有必要功能)

// 1. 配置静态资源(自动配置已失效,需手动映射)
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}

// 2. 配置视图解析器(需手动配置,无默认)
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.thymeleaf()
.prefix("classpath:/templates/")
.suffix(".html")
.encoding("UTF-8");
}

// 3. 添加自定义拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}

2. 底层原理:为什么 @EnableWebMvc 会抛弃自动配置?

核心原因是 Spring Boot 的 WebMvcAutoConfiguration 有 “条件注解” 限制,当存在 WebMvcConfigurationSupport 类型的 Bean 时,自动配置会失效。

关键源码拆解:
  1. @EnableWebMvc 注解:导入 DelegatingWebMvcConfiguration 类:

    1
    2
    3
    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    }
  2. DelegatingWebMvcConfiguration 类:继承自 WebMvcConfigurationSupport

    1
    2
    3
    4
    @Configuration
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    // 基于原生 Spring MVC 规则配置 MVC,无自动配置逻辑
    }
  3. WebMvcAutoConfiguration 的条件注解

    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    @ConditionalOnWebApplication
    @ConditionalOnClass({Servlet.class, DispatcherServlet.class})
    // 关键条件:当 Spring 容器中不存在 WebMvcConfigurationSupport 类型的 Bean 时,才加载自动配置
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    public class WebMvcAutoConfiguration {
    // 自动配置逻辑(静态资源、视图解析器等)
    }
核心逻辑:
  • 添加 @EnableWebMvc → 导入 DelegatingWebMvcConfiguration → 该类继承 WebMvcConfigurationSupport
  • WebMvcAutoConfiguration@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 条件被打破(容器中已存在 WebMvcConfigurationSupport);
  • 最终:WebMvcAutoConfiguration 不加载,Spring Boot 的 MVC 自动配置全部失效,需手动实现所有必要配置。

3. 注意事项:继承 WebMvcConfigurationSupport 的风险

若直接继承 WebMvcConfigurationSupport(而非实现 WebMvcConfigurer),会产生与 @EnableWebMvc 相同的效果 —— 导致自动配置失效:

1
2
3
4
5
// 风险:直接继承 WebMvcConfigurationSupport,自动配置失效
@Configuration
public class RiskyMvcConfig extends WebMvcConfigurationSupport {
// 需手动配置所有 MVC 功能,无默认值
}

建议:仅在 “完全自定义” 场景下使用 @EnableWebMvc 或继承 WebMvcConfigurationSupport,普通扩展优先实现 WebMvcConfigurer

两种配置方式的对比与适用场景

配置方式 核心特点 适用场景 优点 缺点
保留自动配置(无 @EnableWebMvc) 基于自动配置扩展,自定义配置叠加生效 大部分场景(添加拦截器、调整静态资源、自定义消息转换器) 开发效率高,无需重复配置基础功能 无法完全重写自动配置的核心逻辑(如 DispatcherServlet 映射)
完全自定义(有 @EnableWebMvc) 抛弃自动配置,完全手动配置 特殊场景(自定义 DispatcherServlet、重写所有拦截器链) 灵活性极高,可完全掌控 MVC 配置 开发成本高,需手动配置所有基础功能(如静态资源、视图解析器)

常见问题与解决方案

1. 自定义拦截器不生效

问题原因:
  • 未排除静态资源路径,拦截器拦截了 CSS/JS 请求,导致页面样式失效;
  • 拦截器未注册到 InterceptorRegistry(忘记重写 addInterceptors);
  • 配置类未添加 @Configuration 注解,未被 Spring 扫描为 Bean。
解决方案:
1
2
3
4
5
6
7
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
// 必须排除静态资源和登录相关路径
.excludePathPatterns("/static/**", "/login", "/user/login");
}

2. 静态资源访问 404(完全自定义场景)

问题原因:
  • 添加 @EnableWebMvc 后,自动配置失效,未手动配置静态资源映射。
解决方案:
1
2
3
4
5
6
7
8
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 手动映射静态资源(多个路径可多次调用 addResourceHandler)
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("/public/**")
.addResourceLocations("classpath:/public/");
}

3. 视图解析器不生效(Thymeleaf 页面 404)

问题原因:
  • 完全自定义场景下,未配置 Thymeleaf 视图解析器;
  • 前缀 / 后缀配置错误(如路径拼写错误 classpath:/template/ 少个 s)。
解决方案:
1
2
3
4
5
6
7
8
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// 确保导入 Thymeleaf 依赖(spring-boot-starter-thymeleaf)
registry.thymeleaf()
.prefix("classpath:/templates/") // 路径正确,末尾无斜杠
.suffix(".html")
.encoding("UTF-8");
}

总结

Spring Boot 扩展 Spring MVC 的核心是 “按需选择配置方式”:

  1. 保留自动配置(推荐):实现 WebMvcConfigurer 接口,重写需要的方法,基于自动配置叠加自定义逻辑,开发效率高,适合大部分场景;
  2. 完全自定义:添加 @EnableWebMvc 或继承 WebMvcConfigurationSupport,抛弃自动配置,手动实现所有 MVC 功能,适合特殊需求场景

欢迎关注我的其它发布渠道