版本说明:本文基于Spring Boot 3.x源码进行分析。Spring Boot 3.x 基于 Jakarta EE 9+,因此 Servlet API 包名为
jakarta.servlet(而非早期版本中的javax.servlet)。
一、引言
在传统的 Spring MVC 开发中,开发者需要在web.xml中手动配置DispatcherServlet,指定其映射路径、加载配置文件等。而使用 Spring Boot 之后,只需引入spring-boot-starter-web依赖,DispatcherServlet便自动完成配置和注册,开发者可以直接编写 Controller 开始 Web 开发。
这一切的奥秘,就藏在DispatcherServletAutoConfiguration这个自动配置类中。本文将深入解读该类源码,剖析 Spring Boot 是如何在幕后完成DispatcherServlet的自动装配的。
二、类概述与定位
DispatcherServletAutoConfiguration位于org.springframework.boot.autoconfigure.web.servlet包下,是 Spring Boot 对 SpringDispatcherServlet的自动配置实现。该类适用于两种场景:
- 独立应用(Standalone Application):使用嵌入式 Web 服务器(如 Tomcat、Jetty、Undertow)的场景;
- 可部署应用(Deployable Application):使用
SpringBootServletInitializer部署到外部容器的场景。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@AutoConfiguration(after=ServletWebServerFactoryAutoConfiguration.class)@ConditionalOnWebApplication(type=Type.SERVLET)@ConditionalOnClass(DispatcherServlet.class)publicclassDispatcherServletAutoConfiguration{// ...}2.1 核心注解解读
| 注解 | 作用 |
|---|---|
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) | 指定该自动配置类拥有最高优先级,优先于其他自动配置执行 |
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class) | 在 Web 服务器工厂自动配置之后执行,确保 Servlet 容器已就绪 |
@ConditionalOnWebApplication(type = Type.SERVLET) | 仅在Servlet 类型的 Web 应用中生效(区别于 Reactive Web) |
@ConditionalOnClass(DispatcherServlet.class) | 仅当类路径中存在DispatcherServlet时才生效 |
2.2 两个关键常量
publicstaticfinalStringDEFAULT_DISPATCHER_SERVLET_BEAN_NAME="dispatcherServlet";publicstaticfinalStringDEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME="dispatcherServletRegistration";DEFAULT_DISPATCHER_SERVLET_BEAN_NAME:DispatcherServlet实例在 Spring 容器中的 Bean 名称,默认为dispatcherServlet。DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME:ServletRegistrationBean在 Spring 容器中的 Bean 名称,用于向 Servlet 容器注册DispatcherServlet。
三、DispatcherServletConfiguration — 创建 DispatcherServlet 实例
DispatcherServletAutoConfiguration内部定义了第一个静态内部配置类DispatcherServletConfiguration,负责创建DispatcherServlet的 Bean 实例。
@Configuration(proxyBeanMethods=false)@Conditional(DefaultDispatcherServletCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)protectedstaticclassDispatcherServletConfiguration{// ...}3.1 条件注解分析
@Configuration(proxyBeanMethods = false):Spring Boot 2.2+ 引入的优化配置。当设为false时,Spring 不会为该配置类创建 CGLIB 代理,所有@Bean方法的调用将绕过容器的单例管理,每次调用都会执行方法体本身(而非从容器中返回已缓存的单例)。对于DispatcherServletAutoConfiguration这类轻量级配置类,关闭代理可以减少启动时的字节码生成开销,提升启动速度。@Conditional(DefaultDispatcherServletCondition.class):自定义条件,判断是否需要创建默认的DispatcherServlet(下文详述);@ConditionalOnClass(ServletRegistration.class):仅当ServletRegistration类存在时生效;@EnableConfigurationProperties(WebMvcProperties.class):启用WebMvcProperties配置属性绑定,即spring.mvc前缀的配置项。
3.2 dispatcherServlet() 方法
@Bean(name=DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)publicDispatcherServletdispatcherServlet(WebMvcPropertieswebMvcProperties){DispatcherServletdispatcherServlet=newDispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());returndispatcherServlet;}该方法创建一个DispatcherServlet实例,并从WebMvcProperties(即application.properties/application.yml中以spring.mvc开头的配置)中读取四个关键参数:
spring.mvc.dispatch-options-request:是否分发 OPTIONS 请求;spring.mvc.dispatch-trace-request:是否分发 TRACE 请求;spring.mvc.publish-request-handled-events:是否发布请求处理完成事件;spring.mvc.log-request-details:是否记录请求详情(用于调试)。
3.3 multipartResolver() 方法
@Bean@ConditionalOnBean(MultipartResolver.class)@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)publicMultipartResolvermultipartResolver(MultipartResolverresolver){returnresolver;}该方法的作用是检测并适配用户自定义的MultipartResolver:
@ConditionalOnBean(MultipartResolver.class):仅当容器中已存在MultipartResolver类型的 Bean 时生效;@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME):仅当不存在名为multipartResolver(即DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME的值)的 Bean 时生效。
设计意图:如果用户创建了MultipartResolver但命名不正确,Spring Boot 会自动将其"适配"为DispatcherServlet期望的标准名称multipartResolver。
边界场景说明:需要注意的是,该方法的参数为MultipartResolver resolver(而非ObjectProvider<MultipartResolver>)。这意味着当容器中存在多个MultipartResolver类型的 Bean 时,Spring 在注入方法参数时可能抛出NoUniqueBeanDefinitionException。@ConditionalOnBean(MultipartResolver.class)仅检查"至少存在一个",并未限制"恰好存在一个"。因此,如果用户自定义了多个MultipartResolverBean,需要配合@Primary或@Qualifier指定优先级,以避免注入冲突。
四、DispatcherServletRegistrationConfiguration — 注册 DispatcherServlet
第二个内部静态配置类DispatcherServletRegistrationConfiguration负责将DispatcherServlet注册到 Servlet 容器中。
@Configuration(proxyBeanMethods=false)@Conditional(DispatcherServletRegistrationCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)@Import(DispatcherServletConfiguration.class)protectedstaticclassDispatcherServletRegistrationConfiguration{// ...}注意@Import(DispatcherServletConfiguration.class):这意味着注册配置会导入创建配置,确保DispatcherServlet实例优先于注册 Bean 被创建。
4.1 dispatcherServletRegistration() 方法
@Bean(name=DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value=DispatcherServlet.class,name=DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)publicDispatcherServletRegistrationBeandispatcherServletRegistration(DispatcherServletdispatcherServlet,WebMvcPropertieswebMvcProperties,ObjectProvider<MultipartConfigElement>multipartConfig){DispatcherServletRegistrationBeanregistration=newDispatcherServletRegistrationBean(dispatcherServlet,webMvcProperties.getServlet().getPath());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());multipartConfig.ifAvailable(registration::setMultipartConfig);returnregistration;}该方法创建了一个DispatcherServletRegistrationBean,其关键逻辑包括:
- 映射路径:从
WebMvcProperties中读取spring.mvc.servlet.path,默认为/(即处理所有请求); - Servlet 名称:设置为
dispatcherServlet; - 启动加载顺序:从
spring.mvc.servlet.load-on-startup读取,默认值为-1(表示首次请求时初始化); - 文件上传配置:如果容器中存在
MultipartConfigElement,则自动注入,用于处理文件上传。
DispatcherServletRegistrationBean是ServletRegistrationBean的子类,而ServletRegistrationBean实现了ServletContextInitializer接口。在 Spring Boot 启动过程中,所有ServletContextInitializer会被收集并执行,从而将DispatcherServlet真正注册到 Servlet 容器中。
五、自定义条件类深度解析
DispatcherServletAutoConfiguration中定义了两个自定义条件类,它们共同决定了自动配置是否生效。
5.1 DefaultDispatcherServletCondition
@Order(Ordered.LOWEST_PRECEDENCE-10)privatestaticfinalclassDefaultDispatcherServletConditionextendsSpringBootCondition{@OverridepublicConditionOutcomegetMatchOutcome(ConditionContextcontext,AnnotatedTypeMetadatametadata){// 核心判断逻辑}}该条件的匹配逻辑如下:
| 场景 | 结果 | 原因 |
|---|---|---|
容器中已有名为dispatcherServlet的DispatcherServletBean | 不匹配 | 用户已自定义,无需自动创建 |
容器中已有名为dispatcherServlet的非DispatcherServletBean | 不匹配 | 名称冲突,避免覆盖用户定义 |
容器中不存在名为dispatcherServlet的 Bean,且不存在其他名称的DispatcherServletBean | 匹配 | 需要自动创建 |
容器中有DispatcherServletBean,但名称都不是dispatcherServlet | 匹配 | 存在其他名称的DispatcherServlet,但仍需创建默认名称的实例 |
上述四种场景覆盖了所有可能性,彼此之间构成完整的逻辑划分:
- 第一、二行覆盖了"存在名为
dispatcherServlet的 Bean"的所有情况(无论类型是否为DispatcherServlet),结果均为不匹配; - 第三、四行覆盖了"不存在名为
dispatcherServlet的 Bean"的所有情况(无论是否存在其他名称的DispatcherServlet),结果均为匹配。
设计思想:该条件保证 Spring Boot不会覆盖用户自定义的DispatcherServlet,同时也兼顾了容器中可能存在多个DispatcherServlet实例的复杂场景。
5.2 DispatcherServletRegistrationCondition
该条件的匹配逻辑更为复杂,分为两步:
第一步:检查是否存在名为dispatcherServlet的 Bean(对应源码方法checkDefaultDispatcherName)
该方法存在三种分支:
| 分支 | 条件 | 结果 |
|---|---|---|
| 分支 A | 容器中不存在名为dispatcherServlet的 Bean | 通过第一步检查,继续执行第二步checkServletRegistration() |
| 分支 B | 存在名为dispatcherServlet的 Bean,但类型不是DispatcherServlet | 不匹配(防止错误注册) |
| 分支 C | 存在名为dispatcherServlet的 Bean,且类型是DispatcherServlet | 通过第一步检查,继续执行第二步checkServletRegistration()(用户已自定义DispatcherServlet,但仍需检查是否需要自动注册) |
需要特别说明的是,分支 A 和分支 C 返回"通过第一步检查"后,并不意味着整个条件已经匹配通过,代码会继续执行checkServletRegistration()方法进行第二步判断。两步都返回match时,DispatcherServletRegistrationConfiguration才会最终生效。
第二步:检查 ServletRegistrationBean 是否存在(对应源码方法checkServletRegistration)
| 场景 | 结果 |
|---|---|
没有任何ServletRegistrationBean,且没有名为dispatcherServletRegistration的 Bean | 匹配 |
存在名为dispatcherServletRegistration的ServletRegistrationBean | 不匹配(用户已自定义) |
存在名为dispatcherServletRegistration的非ServletRegistrationBean | 不匹配(名称冲突) |
存在其他ServletRegistrationBean,但没有名为dispatcherServletRegistration的 | 匹配 |
设计思想:该条件保证注册过程同样尊重用户的自定义配置,避免自动注册与用户手动注册发生冲突。
六、总结
通过源码分析,我们可以总结出DispatcherServletAutoConfiguration的核心设计思想:
6.1 开箱即用,零配置启动
只需引入spring-boot-starter-web,DispatcherServletAutoConfiguration便会自动创建并注册DispatcherServlet,映射到根路径/,开发者无需任何额外配置即可开始 Web 开发。
6.2 条件化控制,尊重用户配置
通过精心设计的条件注解和自定义条件类,Spring Boot 实现了“用户自定义优先”的策略:
- 如果用户已经创建了
DispatcherServlet,自动配置不会覆盖; - 如果用户已经注册了
ServletRegistrationBean,自动配置不会重复注册。
6.3 职责分离,高内聚低耦合
将"创建实例"和"注册到容器"拆分为两个独立的配置类和两个独立的 Bean,使得每个步骤都可以被独立地条件化控制,也便于用户进行部分覆盖。
6.4 配置属性驱动,灵活可调
通过@EnableConfigurationProperties(WebMvcProperties.class),将spring.mvc.*配置项与DispatcherServlet的行为参数绑定,开发者可以通过配置文件灵活调整DispatcherServlet的各项行为,无需修改代码。
DispatcherServletAutoConfiguration是 Spring Boot 自动配置哲学的典范之作——它用简洁优雅的代码,实现了复杂而灵活的条件化自动装配,让开发者真正享受到"开箱即用"的便捷体验。