该文章较多源码分析))

Web 场景中的自动配置

如何使用 Spring Boot,进行 web 开发

Spring Boot 的 Web 开发能力,由 SpringMVC 提供,给予了 Spring MVC 自动配置的能力

一般来说,在 Spring Initliazer 中,勾选一个web starter 场景,再选上一个 Lombok 就可以了。

前面说过,Spring Boot 将会帮我们自动配置好 web 开发场景中需要的相关配置,只需要在配置文件中指定少量配置就可以运行起来了,我们只需要注意在业务代码中就可以了。

自动配置

Spring Boot 的自动配置类是其核心特性之一,通过 @EnableAutoConfiguration 注解和 AutoConfigurationImportSelector 机制,会从类路径下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.imports 文件中加载自动配置类

  1. 整合 Web 场景

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  2. 引入了 autoconfigure功能

  3. @EnableAutoConfiguration 会使用@Import(AutoConfigurationImportSelector.class)批量导入一些组件

  4. 加载META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.imports文件中配置的所有组件

  5. 大约重要的所有自动配置类如下

    • Web 相关自动配置类

      • org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration 配置 Spring MVC 的核心组件,如控制器、视图解析器、静态资源处理等。

      • org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration 配置嵌入式 Servlet 容器(如 Tomcat、Jetty)的工厂类。

      • org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration 配置 HTTP 消息转换器(如 JSON、XML 转换)。

      • org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration 配置错误处理页面和异常处理机制。

      • org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration

        基于 Spring WebFlux,支持 Netty 服务器的响应式编程模型,新增对 WebAssembly 部署的支持。

      • org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration

        默认使用 Jackson 2.16+,新增对 JSON Schema 自动生成的支持(通过 @Schema 注解)。

      • org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration (响应式 Web)配置 Reactive 模式的 Web 服务器工厂。

      • org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration 配置 WebSocket 的 Servlet 支持。

      • org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration 配置响应式 WebSocket 支持。

    • 数据访问相关自动配置类

      • org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 配置数据源(DataSource),支持 JDBC 连接池。默认连接池为 HikariCP 5.0。
      • org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration 配置 JDBC Template,简化数据库操作。
      • org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration 配置 JPA(Hibernate 实现),支持实体映射和数据库操作。
      • org.springframework.boot.autoconfigure.mybatis.MybatisAutoConfiguration (MyBatis 框架)配置 MyBatis 集成,自动扫描 mapper 接口和 XML 配置。
      • org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration 配置 Redis 缓存连接和操作组件。
      • org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration 配置 MongoDB 数据库连接和操作组件。
      • org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration 配置 Cassandra 数据库连接。
      • org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchAutoConfiguration 配置 Elasticsearch 搜索引擎连接。
    • 缓存与消息中间件相关

      • org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration 配置 Spring 缓存抽象(Cache),支持多种缓存实现(如 Redis、Caffeine)。
      • org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration 配置 Kafka 消息中间件的生产者和消费者。
      • org.springframework.boot.autoconfigure.rabbit.RabbitAutoConfiguration 配置 RabbitMQ 消息中间件的连接和组件。
      • org.springframework.boot.autoconfigure.activemq.ActiveMQAutoConfiguration 配置 ActiveMQ 消息中间件。
    • 安全与认证相关

      • org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration 配置 Spring Security 的基本安全规则。
      • org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration 配置用户认证服务(UserDetailsService)。
      • org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration 配置 OAuth 2.0 客户端认证。
      • org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerAutoConfiguration 配置 OAuth 2.0 资源服务器。
      • org.springframework.boot.autoconfigure.session.SessionAutoConfiguration 配置 HTTP 会话(Session)管理,支持内存、Redis 等存储方式。
    • 日志与监控相关

      • org.springframework.boot.autoconfigure.logging.LoggingAutoConfiguration 配置日志系统(默认使用 Logback),支持日志级别、输出格式等。

      • org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration 配置 JMX 管理功能,用于应用监控。

      • org.springframework.boot.autoconfigure.actuate.metrics.MetricsAutoConfiguration 配置应用指标收集(如 CPU、内存、HTTP 接口耗时)。

      • org.springframework.boot.autoconfigure.actuate.web.WebEndpointAutoConfiguration 配置 Spring Boot Actuator 的 Web 端点(如 /health/info)。

      • org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration 配置数据库变更管理工具 Liquibase,支持版本控制。

      • org.springframework.boot.autoconfigure.actuate.observability.ObservabilityAutoConfiguration

        内置 Micrometer 1.11.0 和 OTel SDK 1.24.0,自动配置应用指标、链路追踪(默认采样率 100%)。

    这里只列出了常用的,在 Maven 项目中,查看 spring-boot-autoconfigure 依赖的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.imports 文件,该文件列出了所有可用配置类(3.5.0 约包含 200+ 个配置类)。

  6. 绑定了配置文件的一堆配置项

    1. Spring MVC 的所有配置 spring.mvc
    2. Web 场景通用的配置 spring.web
    3. 文件上传配置 spring.servlet.multipart
    4. 服务器的一些配置 server 比如编码方式

    如何找:

    image-20250528151243616
image-20250528151256719

默认效果

我们上面说了Spring Boot 对 Spring MVC 的自动配置(AutoConfiguration)机制,那么如何在保持默认配置的同时进行自定义扩展,或者完全接管 MVC 配置。所以我们要了解 Spring Boot 对 Spring MVC 的默认配置

Spring Boot 通过 WebMvcAutoConfiguration 类为 Spring MVC 提供了默认配置,主要包含以下默认效果

  1. 包含了 ContentNegotiatingViewResolverBeanNameViewResolver组件,方便视图解析
    • ContentNegotiatingViewResolver 会根据请求的 Accept 头(如 application/jsontext/html)选择合适的视图解析器,支持内容协商(如返回 JSON 或 HTML)。
    • BeanNameViewResolver 按 Bean 名称解析视图(例如,控制器返回的视图名与 Bean 名称匹配时生效)
  2. 默认的静态资源处理机制:静态资源放在 static 文件夹下即可直接访问
  3. 自动注册了 Converter,GenericConverter,Formatter 组件,适配常见的数据类型转换和格式化需求。
    • Converter:类型转换器(如字符串转日期)。
    • GenericConverter:通用类型转换器(支持更复杂的类型转换逻辑)。
    • Formatter:格式化器(如日期格式化 yyyy-MM-dd)。
  4. 支持 HttpMessageConverters,可以方便返回 json 等数据类型
    • HttpMessageConverters 自动配置 JSON(默认使用 Jackson)、XML(JAXB)等消息转换器,支持 @RequestBody@ResponseBody
  5. 注册 MessageCodesResolver,方便国际化及错误消息处理
    • MessageCodesResolver 处理国际化消息和错误码(如表单验证错误),配合 messages.properties 使用。
  6. 支持静态index.html,若存在 static/index.html,则自动映射为根路径 / 的默认页面
  7. 自动使用 ConfigurableWebBindingInitializer,实现消息处理、数据绑定、类型转化等功能
    • ConfigurableWebBindingInitializer 初始化数据绑定器,支持自定义属性编辑器、验证器等。

重要

  • 如果想保持 boot mvc 的默认配置,并且自定义更多的 mvc 配置,如:interceptors, formatters, view controllers 等,可以使用 @Configuration 注解添加一个 WebMvcConfigurer 类型的配置类, 并不要标注 @EnableWebMvc

  • 如果想保持 boot mvc 的默认配置,但要自定义核心组件实例,比如: RequestMappingHandlerMapping, RequestMappingHandlerAdapter, 或 ExceptionHandlerExceptionResolver,给容器中放一个 WebMvcRegistrations 组件即可

  • 如果想全面接管 Spring MVC,@Configuration 标注一个配置类,并加上 @EnableWebMvc 注解,实现 WebMvcConfigurer 接口

Spring MVC 的请求处理流程

  • 客户端发送 HTTP 请求 → DispatcherServlet 接收请求 → 通过 RequestMappingHandlerMapping 找到对应的控制器方法 → 执行方法并返回结果。

  • RequestMappingHandlerMapping 是这个流程的核心组件,决定了请求如何被路由到具体方法。

自定义 MVC 配置的三种方式

  1. 扩展默认配置(推荐方式)
  • 方式:创建 @Configuration 类并实现 WebMvcConfigurer 接口,不添加 @EnableWebMvc

  • 效果:保持 Spring Boot 的默认配置,仅添加或修改部分功能(如拦截器、格式化器)。

  • 可扩展的常用功能

    • 拦截器(addInterceptors
    • 格式化器 / 转换器(addFormatters
    • 视图控制器(addViewControllers
    • 静态资源处理器(addResourceHandlers
    • CORS 配置(addCorsMappings
  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Configuration
    public class MyWebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
    registry.addFormatter(new MyDateFormatter());
    }
    }
  1. 自定义核心 MVC 组件

    • 方式:创建 WebMvcRegistrations 类型的 Bean,覆盖特定组件的创建逻辑。

      • WebMvcRegistrations 是 Spring MVC 提供的一个扩展接口,允许开发者替换默认的核心 MVC 组件(如请求映射处理器、请求适配器、异常解析器等),同时保留 Spring Boot 的其他自动配置
    • 效果:保持默认配置,但替换核心组件(如 RequestMappingHandlerMapping)。

    • 可自定义的核心组件

      • RequestMappingHandlerMapping(请求映射处理器)
      • RequestMappingHandlerAdapter(请求处理适配器)
      • ExceptionHandlerExceptionResolver(异常处理解析器)
    • 示例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      @Configuration
      public class MyWebMvcRegistrations implements WebMvcRegistrations {
      // RequestMappingHandlerMapping:负责将 HTTP 请求映射到对应的控制器方法(即处理 @RequestMapping、@GetMapping 等注解)
      // 通过自定义 RequestMappingHandlerMapping,可以改变请求映射的默认行为(如添加自定义拦截规则、修改路径匹配策略等)。
      @Override
      public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
      return new CustomRequestMappingHandlerMapping();
      }
      }
  2. 完全接管 MVC 配置

    • 方式:创建 @Configuration 类并添加 @EnableWebMvc 注解,实现 WebMvcConfigurer 接口。**

    • 效果:禁用 Spring Boot 的所有 MVC 自动配置,完全由开发者手动配置

    • 注意:启用 @EnableWebMvc 后,Spring Boot 的所有 MVC 默认配置(如静态资源处理、内容协商)将失效,需手动配置所有功能。

    • 示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      @Configuration
      @EnableWebMvc // 禁用 Spring Boot 的 MVC 自动配置
      public class MyWebMvcConfig implements WebMvcConfigurer {
      @Override
      public void configureViewResolvers(ViewResolverRegistry registry) {
      // 手动配置视图解析器
      registry.jsp("/WEB-INF/views/", ".jsp");
      }

      @Override
      public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
      // 手动配置消息转换器
      converters.add(new MappingJackson2HttpMessageConverter());
      }
      }
全自动 直接编写控制器逻辑 全部使用自动配置默认效果
手自一体 @Configuration + 配置 WebMvcConfigurer + 配置 WebMvcRegistrations 不要标注@EnableWebMvc 自动配置效果 手动设置部分功能 定义 MVC 底层组件
全手动 @Configuration + 配置 WebMvcConfigurer 标注@EnableWebMvc 禁用自动配置效果 全手动设置
场景 方式 注解 接口 / 组件
扩展默认配置(如添加拦截器) 扩展配置 @Configuration WebMvcConfigurer
替换核心组件(如自定义请求映射) 自定义组件 @Configuration WebMvcRegistrations
完全控制 MVC 配置(如使用 JSP 视图) 完全接管 @Configuration+@EnableWebMvc WebMvcConfigurer

总结:所有的自定义模式,给容器类放一个配置类 @Configuration 实现 WebMvcConfigurer,不要标注@EnableWebMvc注解,实现了手自一体的模式。

两种模式

  1. 前后分离模式:@RestController 响应 JSON 数据

    将前端和后端作为两个独立的应用进行开发和部署。前端专注于页面展示和用户交互,通过 API 与后端进行数据交互;后端专注于业务逻辑处理、数据存储与读取,仅提供 RESTful API 接口给前端调用。

    • 前端:使用现代前端框架如 Vue.js、React、Angular 等构建单页应用(SPA)或多页应用(MPA) 。以 Vue.js 为例,通过 Vue Router 进行路由管理,利用 Axios 等工具发起 HTTP 请求获取后端数据,然后在组件中进行数据渲染和交互逻辑处理。
    • 后端:使用 @RestController 注解标识控制器类,该注解组合了 @Controller@ResponseBody,表示方法返回值会直接序列化为 JSON 等格式响应给前端。后端基于 Spring Boot 等框架搭建 RESTful API,例如定义接口 /api/user/login 用于用户登录验证,返回包含用户信息的 JSON 数据。
  2. 前后不分离模式:@Controller + Thymeleaf 模板引擎

    前端页面的渲染由后端直接处理,后端将数据填充到模板中,然后返回完整的 HTML 页面给浏览器。这种模式下,前后端的耦合度较高。

    • 后端:使用 @Controller 注解标识控制器类,配合 Thymeleaf、Freemarker 等模板引擎。以 Thymeleaf 为例,在控制器方法中获取业务数据,如查询用户信息,然后将数据模型传递给 Thymeleaf 模板。Thymeleaf 模板中通过特定语法(如 th:textth:each 等)将数据渲染到 HTML 页面中,最终返回完整渲染后的 HTML 页面给前端浏览器。
    • 前端:主要负责页面的基本样式和简单交互,如 HTML 结构搭建、CSS 样式编写,JavaScript 实现简单的表单验证等。相比前后分离模式,前端的逻辑处理相对简单。

Spring Boot 对静态资源的映射配置规则

先来讲解一下 Spring Boot 对于静态资源的相关配置规则,我们现在暂时选择来开发一个前后端没有分离的很彻底的应用,如果前后端分离的很彻底,我们只用 Spring Boot 来做后端接口,就不涉及到 Spring Boot 是如何管理静态资源方面的内容了。

WebMvcAutoConfiguration 原理

这个自动配置类的写法如下

生效条件

1
2
3
4
5
6
7
8
9
10
11
12
@AutoConfiguration(
after = {DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class}
) // 在这些自动配置之后
@ConditionalOnWebApplication(
type = Type.SERVLET
) // 如果是 Web 应用就生效,类似 Servlet,Reactive响应式
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class}) // 当类路径下存在这三个类才生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class}) // 规定当 Spring 容器中不存在这个类及其子类的 Bean 才会生效时才会生效
@AutoConfigureOrder(-2147483638) // 指定自动配置类的加载顺序,数值越小优先级越高
@ImportRuntimeHints({WebResourcesRuntimeHints.class}) // 引入运行时提示相关的配置类。
public class WebMvcAutoConfiguration {
}

以下是详细解释

  1. @AutoConfiguration(after = {DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
    • 含义@AutoConfiguration 是 Spring Boot 用于标识自动配置类的注解 。after 属性指定了该自动配置类的加载顺序,即 WebMvcAutoConfiguration 会在 DispatcherServletAutoConfigurationTaskExecutionAutoConfigurationValidationAutoConfiguration 之后进行加载。这确保了在配置 Spring MVC 相关功能时,其依赖的前置组件(如 DispatcherServlet 相关配置、任务执行配置、验证配置)已经完成配置。
    • 作用:保证配置顺序正确,避免因依赖组件未配置而导致的错误,使整个自动配置流程有序进行。
  2. @ConditionalOnWebApplication(type = Type.SERVLET)
    • 含义@ConditionalOnWebApplication 是条件注解,只有当应用是 Web 应用时才会生效。type = Type.SERVLET 限定了仅在 Servlet 类型的 Web 应用场景下该自动配置类才会起作用,像基于 Spring WebFlux 的响应式 Web 应用(Type.REACTIVE)则不会触发此配置。
    • 作用:精准定位适用场景,避免在非 Servlet 类型的 Web 应用中加载不必要的 Spring MVC 配置,提升应用启动性能和配置准确性。
  3. @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
    • 含义:该条件注解表示只有当类路径下存在 Servlet.classDispatcherServlet.classWebMvcConfigurer.class 这几个类时,WebMvcAutoConfiguration 才会生效。Servlet 是 Java Web 开发基础规范类;DispatcherServlet 是 Spring MVC 中负责接收请求并分发到对应处理器的核心 servlet;WebMvcConfigurer 是用于自定义 Spring MVC 配置的接口。这意味着只有引入了相关依赖(如 Spring MVC 依赖),确保这些类存在于项目中,才会进行 Spring MVC 的自动配置。
    • 作用:根据项目实际引入的依赖来判断是否进行 Spring MVC 相关配置,防止在没有相关依赖的项目中错误加载配置,保证配置的有效性和针对性。
  4. @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
    • 含义:此注解规定当 Spring 容器中不存在 WebMvcConfigurationSupport 及其子类的 Bean 时,WebMvcAutoConfiguration 才会生效。如果开发者自定义了继承 WebMvcConfigurationSupport 的配置类,Spring Boot 认为开发者要对 Spring MVC 进行全面自定义,就会跳过 WebMvcAutoConfiguration 的自动配置。
    • 作用:平衡自动配置和开发者自定义配置的关系,让开发者有能力完全接管 Spring MVC 配置,同时在未进行深度自定义时享受自动配置带来的便利。
  5. @AutoConfigureOrder(-2147483638)
    • 含义@AutoConfigureOrder 用于指定自动配置类的加载顺序,数值越小优先级越高。这里设置为 -2147483638,表明它在自动配置类加载顺序中处于较高优先级,能在一些依赖它的配置之前完成加载。
    • 作用:保证在整个自动配置体系中,Spring MVC 的相关配置能按照预期顺序加载,避免因顺序问题导致的配置冲突或错误。
  6. @ImportRuntimeHints({WebResourcesRuntimeHints.class})
    • 含义@ImportRuntimeHints 注解用于引入运行时提示相关的配置类。WebResourcesRuntimeHints.class 会提供关于 Web 资源(如静态资源)在运行时的一些提示信息,帮助应用在运行时更高效地处理和管理这些资源,比如优化资源的查找、加载等操作。
    • 作用:提升应用在运行时对 Web 资源的处理效率和性能,确保资源相关功能的正确执行。

效果

那么生效之后是什么效果

放了两个 Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnBooleanProperty({"spring.mvc.hiddenmethod.filter.enabled"})
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}

@Bean
@ConditionalOnMissingBean({FormContentFilter.class})
@ConditionalOnBooleanProperty(
name = {"spring.mvc.formcontent.filter.enabled"},
matchIfMissing = true
)
public OrderedFormContentFilter formContentFilter() {
return new OrderedFormContentFilter();
}
  • HiddenHttpMethodFilter:页面表单可以提交 Rest 请求(GET,POST,PUT,DELETE)
  • FormContentFilter:表单内容 Filter,只有 GET(数据发url后) POST(数据放请求体) 请求可以携带数据,PUT,DELETE的请求体数据会被忽略

效果的第一个是这个

该类作为 Spring Boot 自动配置和开发者自定义配置的桥梁,既加载默认的 MVC 配置(通过 EnableWebMvcConfiguration),又允许开发者通过实现 WebMvcConfigurer 接口进行扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 声明该类是一个配置类,不代理 Bean 方法,即每次调用配置类中的 Bean 方法时,直接创建新实例,而非从 Spring 容器中获取
@Configuration(
proxyBeanMethods = false
)
// 导入 EnableWebMvcConfiguration 类,是 Spring MVC 的核心配置类
@Import({EnableWebMvcConfiguration.class})
// 启用配置属性绑定,将 application.properties 或 application.yml 中的配置项映射到 WebMvcProperties 和 WebProperties 类的实例中
@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})
// 指定配置类的加载顺序,数值越小优先级越高
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
....
}
  • 实现接口
    • WebMvcConfigurer: 提供自定义 Spring MVC 配置的回调方法(如添加拦截器、配置消息转换器)。通过实现该接口,可在不使用 @EnableWebMvc 的情况下定制 MVC 功能。
    • ServletContextAware: 允许 Bean 获取 ServletContext 引用,用于访问 Servlet 容器相关信息(如获取应用上下文路径)。

给容器中放了WebMvcConfigurer组件:给 SpringMVC 添加各种定制功能

  • 所有的功能最终会和配置文件进行绑定
  • WebMvcPropertiesspring.mvc配置文件
  • WebPropertiesspring.web配置文件

WebMvcConfigurer

这里是 WebMvcConfigurer 这个接口的源码,提供了配置 Spring MVC 底层的所有组件入口

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
// WebMvcConfigurer接口用于对Spring MVC进行自定义配置
public interface WebMvcConfigurer {

// 配置路径匹配相关策略,比如设置路径匹配的后缀策略、路径分隔符等
default void configurePathMatch(PathMatchConfigurer configurer) {
}

// 配置内容协商策略,例如根据请求头中的Accept字段决定返回的数据格式(如JSON、XML等)
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}

// 异步支持,配置异步支持相关参数,如异步请求的超时时间、任务执行器等
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}

// 配置默认Servlet处理策略,比如当Spring MVC无法处理请求时,是否转发给默认Servlet
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}

// 向FormatterRegistry中添加自定义的格式化器,用于数据类型的格式化和解析,如日期格式化等
default void addFormatters(FormatterRegistry registry) {
}

// 向InterceptorRegistry中添加自定义的拦截器,可用于在请求处理前后执行自定义逻辑,如权限校验、日志记录等
default void addInterceptors(InterceptorRegistry registry) {
}

// 向ResourceHandlerRegistry中添加自定义的资源处理器,用于处理静态资源(如CSS、JS文件)的映射路径等
default void addResourceHandlers(ResourceHandlerRegistry registry) {
}

// 向CorsRegistry中添加跨域资源共享(CORS)的映射配置,解决跨域访问问题
default void addCorsMappings(CorsRegistry registry) {
}

// 向ViewControllerRegistry中添加视图控制器,用于直接映射请求到视图,而无需经过控制器方法处理
default void addViewControllers(ViewControllerRegistry registry) {
}

// 配置视图解析器,比如设置视图解析器的前缀、后缀,选择具体的视图解析器实现类等
default void configureViewResolvers(ViewResolverRegistry registry) {
}

// 向HandlerMethodArgumentResolver列表中添加自定义的处理器方法参数解析器,用于解析控制器方法的参数
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}

// 向HandlerMethodReturnValueHandler列表中添加自定义的处理器方法返回值处理器,用于处理控制器方法的返回值
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}

// 配置HTTP消息转换器,如设置默认的JSON转换器、XML转换器等,决定如何将请求数据转换为对象以及将对象转换为响应数据
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) {
}

// 向ErrorResponse.Interceptor列表中添加自定义的错误响应拦截器,用于在错误响应返回给客户端之前进行处理
default void addErrorResponseInterceptors(List<ErrorResponse.Interceptor> interceptors) {
}

// 获取自定义的验证器,如果返回非null,则使用该验证器进行数据验证
@Nullable
default Validator getValidator() {
return null;
}

// 获取自定义的消息码解析器,用于解析和处理国际化消息码等相关操作
@Nullable
default MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
  • 其中addResourceHandlers就是专门处理静态资源规则的资源处理器,就是来做静态资源映射

addResourceHandlers分析

默认的静态资源规则源码

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
/**
* 配置静态资源处理器,用于处理Web应用中的静态资源请求(如CSS、JS、图片等)
* @param registry 资源处理器注册器,用于注册静态资源处理规则
*/
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 检查是否启用默认静态资源映射(通过spring.web.resources.add-mappings属性控制)
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
// 1. 配置WebJars资源映射
// WebJars是将前端依赖(如jQuery、Bootstrap)打包成JAR的标准,可通过Maven/Gradle引入
this.addResourceHandler(
registry,
this.mvcProperties.getWebjarsPathPattern(), // 请求路径模式,默认是"/webjars/**"
"classpath:/META-INF/resources/webjars/"// 资源存放位置,WebJars规范定义的默认路径
);

// 2. 配置普通静态资源映射
this.addResourceHandler(
registry,
this.mvcProperties.getStaticPathPattern(), // 请求路径模式,默认是"/**"
(registration) -> {
// 添加静态资源的物理位置(可通过spring.web.resources.static-locations配置)
// 默认位置包括:classpath:/static/、classpath:/public/等
registration.addResourceLocations(this.resourceProperties.getStaticLocations());

// 如果存在ServletContext(即非测试环境),添加ServletContext根路径资源
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
}
);
}
}

规则一:可以看到其中的webjars,这是一种重要的配置规则,访问/webjars/**路径就去classpath:/META-INF/rescources/webjars/下找资源

规则二:访问/**,就去 classpath:/static/等静态资源的默认的四个位置找

1
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

规则三:静态资源默认都有缓存规则的设置

  • 如果说浏览器访问了一个静态资源,服务器这个资源没有发生变化,下次访问的时候可以直接让浏览器用自己缓存中的东西,就不用给浏览器发请求

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
/**
* 注册静态资源处理器,将指定URL模式映射到对应资源位置,并应用缓存配置
*
* @param registry 资源处理器注册器
* @param pattern 请求路径模式(如"/webjars/**"或"/**")
* @param customizer 自定义资源位置的回调函数
*/
private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, Consumer<ResourceHandlerRegistration> customizer) {
// 检查是否已有相同模式的映射,避免重复注册
if (!registry.hasMappingForPattern(pattern)) {
// 创建资源处理器注册,将URL模式映射到资源位置
ResourceHandlerRegistration registration = registry.addResourceHandler(new String[]{pattern});

// 应用自定义资源位置(如classpath:/static/或ServletContext根路径)
customizer.accept(registration);

// 配置缓存策略:
// 1. 设置缓存周期(秒),对应配置项spring.web.resources.cache.period
registration.setCachePeriod(this.getSeconds(this.resourceProperties.getCache().getPeriod()));

// 2. 设置HTTP缓存控制头(如Cache-Control: max-age=3600)
registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());

// 3. 启用Last-Modified时间戳支持(基于文件修改时间验证缓存)
registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());

// 应用自定义配置(如Gzip压缩、资源链处理器等)
this.customizeResourceHandlerRegistration(registration);
}
}

  • 所有缓存的设置,直接通过配置文件:spring.web

  • cachePeriod:缓存周期;多久不用找服务器要新的。默认没有,以 s 为单位

  • cacheControl:HTTP 缓存控制;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching

  • useLastModified:是否使用最后一次修改。

  • 也适用自定义:

    • 配置文件覆盖默认值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 禁用缓存(开发环境常用)
    spring.web.resources.cache.period=0

    # 启用缓存并设置1小时有效期(生产环境)
    spring.web.resources.cache.period=3600

    # 自定义静态资源路径和缓存控制
    spring.web.resources.static-locations=classpath:/custom-static/
    spring.web.resources.cache.cachecontrol.max-age=86400
    • Java 代码自定义 通过实现 WebMvcConfigurer 手动配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/custom/**")
    .addResourceLocations("classpath:/custom-resources/")
    .setCachePeriod(3600) // 覆盖配置文件设置
    .setUseLastModified(true);
    }
    }

HTTP 缓存机制详解

缓存配置的参数

配置项 对应属性 默认值 作用
spring.web.resources.cache.period resourceProperties.getCache().getPeriod() null 缓存过期时间(秒),设置后响应头添加 Cache-Control: max-age=xxx
spring.web.resources.cache.cachecontrol.* resourceProperties.getCache().getCachecontrol() 见下方 细粒度控制 HTTP 缓存头(如 no-cachemust-revalidate
spring.web.resources.cache.use-last-modified resourceProperties.getCache().isUseLastModified() true 是否使用文件修改时间(Last-Modified 头)验证缓存

继续上述在addResourceHandler部分关于缓存的内容中,此方法通过以下方式控制缓存:

强缓存(Cache-Control)

  • 当设置 cachePeriod 时,响应头添加 Cache-Control: max-age=xxx,浏览器直接使用本地缓存,无需请求服务器。

    1
    spring.web.resources.cache.period=3600  # 缓存1小时
  • 协商缓存(Last-Modified)

    • 默认启用 useLastModified,响应头添加 Last-Modified 时间戳。
    • 下次请求时,浏览器发送 If-Modified-Since 头,服务器比较时间戳决定是否返回新资源。
  • 细粒度控制 通过 spring.web.resources.cache.cachecontrol.* 配置:

    1
    2
    3
    spring.web.resources.cache.cachecontrol.max-age=3600  # 等同 cache.period
    spring.web.resources.cache.cachecontrol.no-cache=true # 禁用强缓存
    spring.web.resources.cache.cachecontrol.must-revalidate=true # 缓存过期前必须验证

EnableWebMvcConfiguration 源码

1
2
3
4
5
6
7
8
   // Spring Boot给容器放了WebMvcConfigurationSupport组件
// 如果我们自己放了WebMvcConfigurationSupport 组件,Spring boot 的 WebMvcAutoConfiguration 都会失效
@Configuration(
proxyBeanMethods = false
)
// 将 WebProperties 类与配置文件(如 application.properties)绑定,允许通过配置文件自定义 Web 相关属性。
@EnableConfigurationProperties({WebProperties.class})
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {}
  • DelegatingWebMvcConfiguration 是 Spring MVC 的核心配置类,它实现了 WebMvcConfigurationSupport 并聚合了所有 WebMvcConfigurer 实例的配置。
    • 允许开发者通过实现 WebMvcConfigurer 接口自定义配置,而无需完全接管 MVC 配置(即不使用 @EnableWebMvc)。
  • 通过实现 ResourceLoaderAware 接口,该类可获取 ResourceLoader 实例,用于加载类路径或文件系统中的资源。在配置静态资源处理器时,可通过 ResourceLoader 定位资源位置(如 classpath:/static/)。

使用 webjars

首先 Spring Boot 的有个叫 webjars 的东西,所有通过 pom.xml 引入的静态资源,都在/webjars/**下(这个在静态资源的自动配置类中可以找到),也就是说 Spring Boot 会自动去 classpath:/META-INF/resources/webjars/ 找资源,webjars 以 jar 包的方式引入静态资源(也就是说,我们都可以通过http://localhost:8080/webjars/xx访问对应的静态资源)

各种静态资源 jar 都可以通过 pom.xml 依赖的方式引入 ,我们引入 JQuery 看看,引入的 JQuery 的依赖包下边,可以看到 webjars 内容长这样

现在我们引入了 jQuery 的静态资源,我们启动项目,来试一下能不能通过对应的路径访问

所以通过 SpringBoot 来开发相关的静态资源,非常方便,我们只要引入对应的 webjars 就可以了,Spring Boot 不需要我们进行过多的配置,有自己默认配置的规则。

就是用 pom.xml 中导入依赖,然后可以用 classpath:/META-INF/resources/webjars/找资源

使用resources

当然如果我们想要用自己的静态资源,不想使用webjars的东西,这个时候就需要另外一个映射规则就是/**(也就是说,我们通过http://localhost:8080/xxx.js访问静态资源,这时候就是使用 resources 规则了。

默认静态资源路径

静态资源映射规则此事在WebMvcAutoConfigurationAdapter亦有记载

除了使用 webjars,Spring Boot 默认提供了更直接的静态资源管理方式——直接将静态文件放在项目的 resources 目录下。以下是详细的配置规则和使用案例:

Spring Boot 默认会在以下位置查找静态资源(优先级从高到低):

  1. classpath:/META-INF/resources/
  2. classpath:/resources/
  3. classpath:/static/
  4. classpath:/public/
  5. ServletContext 根目录(如 src/main/webapp/

当浏览器发起 /xxx 请求时,Spring Boot 会依次在这些目录中查找名为 xxx 的文件。

1
2
3
4
5
6
7
8
src/main/resources/
├─ META-INF/resources/ # 优先级最高
├─ resources/ # 优先级次之
├─ static/ # 最常用的静态资源目录
│ ├─ css/
│ ├─ js/
│ └─ images/
└─ public/ # 优先级最低

访问静态资源

假设在 static/images 目录下放置 logo.png,访问方式为:

1
http://localhost:8080/images/logo.png

无需在 URL 中添加 static,直接通过资源路径访问。

例如:在 src/main/resources/static 下创建 index.html

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>Welcome to Spring Boot!</h1>
<img src="/images/spring-logo.png" alt="Spring Logo">
</body>
</html>

将图片 spring-logo.png 放入 static/images/ 目录。

启动应用后:

  • 访问首页:http://localhost:8080/index.html

  • 访问图片:http://localhost:8080/images/spring-logo.png

注意存在的问题:

缓存问题

修改静态资源后,浏览器可能缓存旧文件。可通过以下方式解决:

  • 强制刷新(Ctrl + F5
  • 在资源URL中添加版本号:/css/style.css?v=1.0

自定义静态资源路径

默认静态资源位置

1
2
3
4
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/

自定义单个位置

1
2
3
4
5
# application.yml
spring:
web:
resources:
static-locations: classpath:/custom/

如果默认路径不满足需求,可在 application.properties 中自定义多个位置:

1
2
3
4
5
# 修改静态资源访问前缀
spring.mvc.static-path-pattern=/assets/**

# 添加新的静态资源目录(覆盖默认配置)
spring.web.resources.static-locations=classpath:/custom-static/,classpath:/static/
  1. 创建 resources/custom-static/hello.html
  2. 配置后访问:http://localhost:8080/assets/hello.html

通过 Java 配置自定义:

  • 实现 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
    @Configuration
    public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    // 添加自定义静态资源映射
    registry.addResourceHandler("/assets/**")
    .addResourceLocations("classpath:/custom-assets/")
    .setCachePeriod(3600); // 设置缓存时间

    // 保持默认的静态资源映射
    registry.addResourceHandler("/**")
    .addResourceLocations("classpath:/static/",
    "classpath:/public/",
    "classpath:/custom/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    // 自定义欢迎页控制器
    registry.addViewController("/").setViewName("forward:/index.html");
    registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    }
    }
  • 自定义 ResourceHttpRequestHandler

    ResourceHttpRequestHandler 是 Spring MVC 中的一个类,它是 HttpRequestHandler 的实现类,主要作用是处理静态资源请求,根据配置的资源位置查找请求对应的静态资源文件

    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
    @Configuration 
    public class CustomResourceConfig {
    @Bean
    public SimpleUrlHandlerMapping customResourceHandlerMapping() {
    // 创建一个SimpleUrlHandlerMapping实例,用于处理URL映射
    SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
    // 创建一个HashMap来存储URL和对应的处理器
    Map<String, ResourceHttpRequestHandler> urlMap = new HashMap<>();

    // 创建一个ResourceHttpRequestHandler实例,用于处理资源请求
    ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
    // 设置处理器查找资源的位置,这里指定了两个路径
    handler.setLocationValues(Arrays.asList(
    "classpath:/custom/",
    "classpath:/static/"
    ));

    // 将"/**"路径映射到创建的处理器
    urlMap.put("/**", handler);
    // 设置URL映射关系
    mapping.setUrlMap(urlMap);
    // 设置该映射的顺序,值越大优先级越低,这里设置为Integer.MAX_VALUE - 1
    mapping.setOrder(Integer.MAX_VALUE - 1);

    return mapping;
    }
    }

注意路径匹配规则问题:

  • 避免Controller的请求路径与静态资源名称冲突。例如,若存在 /user 接口,则无法通过 /user.html 访问静态页面。

配置欢迎页映射

静态资源文件夹下的所有index.html页面,会被/映射; 也就是说我们直接访问http://localhost:8080/就直接访问静态资源文件夹中的index.html,相当于我们以前Spring MVC 下面的index.jsp,比如我们现在随机选 public 的静态资源文件夹下创建index.html

源码:

Bean 配置入口

1
2
3
4
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
return (WelcomePageHandlerMapping)this.createWelcomePageHandlerMapping(applicationContext, mvcConversionService, mvcResourceUrlProvider, WelcomePageHandlerMapping::new);
}

这个 Bean 配置方法创建了 WelcomePageHandlerMapping 实例,该实例负责处理欢迎页的路径映射。Spring Boot 通过自动配置机制在容器启动时创建此映射器。

进入到其中的 WelcomePageHandlerMapping

1
2
3
4
5
6
7
8
9
10
11
12
13
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource indexHtmlResource, String staticPathPattern) {
this.setOrder(2);
WelcomePage welcomePage = WelcomePage.resolve(templateAvailabilityProviders, applicationContext, indexHtmlResource, staticPathPattern);
if (welcomePage != WelcomePage.UNRESOLVED) {
logger.info(LogMessage.of(() -> {
return !welcomePage.isTemplated() ? "Adding welcome page: " + String.valueOf(indexHtmlResource) : "Adding welcome page template: index";
}));
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName(welcomePage.getViewName());
this.setRootHandler(controller);
}

}

欢迎页解析部分

1
2
3
4
5
6
7
static WelcomePage resolve(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource indexHtmlResource, String staticPathPattern) {
if (indexHtmlResource != null && "/**".equals(staticPathPattern)) {
return new WelcomePage("forward:index.html", false);
} else {
return templateAvailabilityProviders.getProvider("index", applicationContext) != null ? new WelcomePage("index", true) : UNRESOLVED;
}
}

源码分析:

  • HandlerMapping:根据请求路径/a找那个 handler 能处理请求

    • 是 Spring MVC 中负责根据请求路径找到对应处理器(Handler)的组件
    • WelcomePageHandlerMapping:
      • 访问 /** 路径下的所有请求,都在以前四个静态资源路径下找,欢迎页也一样
      • index.html:只要静态资源的位置有一个 index.html 页面,项目启动默认访问
  • 欢迎页解析:

    • WelcomePage.resolve() 方法会按照以下优先级查找欢迎页:

      • 首先检查是否存在 index 模板文件(如 index.html, index.ftl, index.jsp 等)

      • 如果没有模板,则查找静态资源目录下的 index.html 文件

        • 当存在模板引擎和对应的 index 模板时,会优先使用模板渲染。

          1
          2
          src/main/resources/templates/index.html (Thymeleaf)
          src/main/resources/templates/index.ftl (FreeMarker)
  • 控制器创建: 如果找到欢迎页,创建 ParameterizableViewController 来处理根路径请求

配置自定义

当然,如果我们不想要使用SpringBoot默认的静态资源文件夹,我们还可以定义静态资源的映射

默认情况下,静态资源路径模式为 /**

如需自定义欢迎页行为,可以通过以下方式:

1
2
3
4
5
6
7
# application.yml
spring:
mvc:
static-path-pattern: /static/** # 修改静态资源路径模式
web:
resources:
static-locations: classpath:/custom/ # 自定义静态资源位置
  • static-path-pattern 不是 /** 时,欢迎页功能会被禁用,这是因为 WelcomePage.resolve() 方法中有明确的检查,如果必须修改静态资源路径模式,可以通过自定义控制器来实现欢迎页

  • 自定义控制器的 @RequestMapping("/") 会覆盖默认的欢迎页映射

  • 完全禁用欢迎页

    1
    2
    3
    4
    5
    # application.yml
    spring:
    web:
    resources:
    add-mappings: false # 禁用所有静态资源映射
  • 使用 profile 特定的欢迎页

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Controller
    @Profile("production")
    public class ProductionWelcomeController {

    @RequestMapping("/")
    public String productionWelcome() {
    return "production-index";
    }
    }

    @Controller
    @Profile("development")
    public class DevelopmentWelcomeController {

    @RequestMapping("/")
    public String developmentWelcome() {
    return "dev-index";
    }
    }
  • 要注意了,如果自己定义了静态资源映射之后,默认的文件夹就都不生效了,相当于原来的那些访问方式都不生效了。

Favicon 规则

这个和欢迎页规则差不多,默认情况下在静态资源目录下找favicon.ico

当然,如果我们不想要使用SpringBoot默认的静态资源文件夹,我们还可以定义静态资源的映射

但是这是一个隐形规则,是根据浏览器实现的,不是 Spring Boot让他变的