DispatcherServlet 注册
概述
DispatcherServlet 是 Spring MVC 的核心入口。在 Spring Boot 中,DispatcherServletAutoConfiguration 负责自动创建和注册 DispatcherServlet,而 DispatcherServletRegistrationBean 负责将其注册到内嵌 Servlet 容器中。
本文深入拆解 DispatcherServlet 注册的完整链路,涵盖自动配置顺序、Servlet 注册、URL 映射、延迟加载、Filter 注册等 8 个细节点。
本文基于 Spring Boot 3.x 源码分析。
1. DispatcherServletAutoConfiguration 的 @AutoConfigurationAfter 保证
1.1 源码
java
// DispatcherServletAutoConfiguration.java
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
public class DispatcherServletAutoConfiguration {
// ...
}1.2 为什么需要 @AutoConfigurationAfter
java
// DispatcherServletAutoConfiguration 依赖于 ServletWebServerFactoryAutoConfiguration 的原因:
// ServletWebServerFactoryAutoConfiguration 负责创建:
// - TomcatServletWebServerFactory (或 Jetty/Undertow)
// - 内嵌 Servlet 容器 (Tomcat.start() → 创建 ServletContext)
// DispatcherServletRegistrationBean 需要一个 ServletContext 来注册 Servlet
// 所以必须等 ServletWebServerFactoryAutoConfiguration 执行完毕
// 调用链:
// 1. ServletWebServerFactoryAutoConfiguration 创建内嵌 Tomcat
// → Tomcat.getServletContext() 可用
//
// 2. DispatcherServletAutoConfiguration 注册 DispatcherServlet
// → servletContext.addServlet("dispatcherServlet", dispatcherServlet)
//
// 如果顺序颠倒 → ServletContext 不可用 → NullPointerException1.3 自动配置顺序
ServletWebServerFactoryAutoConfiguration ← 先执行(创建内嵌容器)
│
├─ TomcatServletWebServerFactory
│ └─ getWebServer()
│ └─ Tomcat.getServletContext()
│
└─ 启动内嵌 Tomcat,ServletContext 就绪
DispatcherServletAutoConfiguration ← 后执行(注册 DispatcherServlet)
│
├─ DispatcherServletRegistrationBean
├─ DispatcherServlet
└─ servletContext.addServlet("dispatcherServlet", ...)1.4 @AutoConfigurationOrder 与 @AutoConfigurationAfter 的区别
java
// @AutoConfigurationOrder 作用在 AutoConfiguration.imports 排序
@AutoConfigurationOrder(10)
// 值越小越靠前
// @AutoConfigurationAfter 作用在拓扑排序中
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
// 在 ServletWebServerFactoryAutoConfiguration 之后才加载2. DispatcherServletRegistrationBean 继承结构
2.1 类层次
ServletContextInitializer (接口)
│
└─ RegistrationBean (抽象类)
│
├─ ServletListenerRegistrationBean
│
└─ ServletRegistrationBean (抽象类)
│
├─ DispatcherServletRegistrationBean
│ └─ 专用于 DispatcherServlet
│
├─ FilterRegistrationBean
│
└─ DelegatingFilterProxyRegistrationBean2.2 各层的职责
java
// ServletContextInitializer —— 顶层接口
@FunctionalInterface
public interface ServletContextInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
// RegistrationBean —— 抽象基类
public abstract class RegistrationBean
implements ServletContextInitializer {
private boolean enabled = true; // 是否启用
private int order = Ordered.LOWEST_PRECEDENCE; // 注册顺序
private boolean initialized = false; // 确保只注册一次
@Override
public final void onStartup(ServletContext servletContext)
throws ServletException {
if (!this.enabled) return; // 未启用 → 跳过
if (this.initialized) return; // 已注册 → 跳过
// 1. 配置 Servlet 容器(编码、session 超时等)
configure(servletContext);
// 2. 执行注册(具体由子类实现)
register(servletContext.getServletRegistration());
this.initialized = true;
}
protected abstract void register(String description, ServletContext servletContext);
}
// ServletRegistrationBean —— 将 Servlet 注册到容器的抽象
public abstract class ServletRegistrationBean<T extends Servlet>
extends RegistrationBean {
private T servlet; // 要注册的 Servlet 实例
private Set<String> urlMappings; // URL 映射集合
@Override
protected final void register(String description, ServletContext servletContext) {
// 调用 ServletContext.addServlet()
Dynamic dynamic = servletContext.addServlet(getServletName(), this.servlet);
// 配置 Servlet 注册信息
dynamic.setLoadOnStartup(this.loadOnStartup);
dynamic.addMappingForUrlPatterns(...);
// 配置 Multipart
configureMultipart(dynamic);
}
}
// DispatcherServletRegistrationBean —— DispatcherServlet 专用
public class DispatcherServletRegistrationBean
extends ServletRegistrationBean<DispatcherServlet> {
private final DispatcherServletPath dispatcherServletPath;
public DispatcherServletRegistrationBean(
DispatcherServlet servlet,
DispatcherServletPath dispatcherServletPath) {
super(servlet, "/"); // 默认映射到根路径
this.dispatcherServletPath = dispatcherServletPath;
setName("dispatcherServlet");
}
}3. addRegistration() 的 Servlet 容器注册
3.1 源码
java
// ServletRegistrationBean.java
@Override
protected void register(String description, ServletContext servletContext) {
// 1. 调用 ServletContext.addServlet() 注册
Dynamic dynamic = servletContext.addServlet(getServletName(), this.servlet);
// getServletName() = "dispatcherServlet"
if (dynamic == null) {
// 如果已存在同名 Servlet → 警告(不应该发生)
logger.debug("A Servlet named " + getServletName()
+ " is already registered.");
return;
}
// 2. 设置启动顺序(load-on-startup)
dynamic.setLoadOnStartup(this.loadOnStartup);
// 默认 -1(首次请求时初始化)
// 3. 添加 URL 映射
// 默认映射到 "/"
dynamic.addMappingForUrlPatterns(
this.matchAlways, // 是否始终匹配
!isAsyncSupported(), // 是否异步支持
getUrlMappings().toArray(new String[0]));
// matchAlways = true → 无论请求路径都匹配
// 4. 配置 Multipart
configureMultipart(dynamic);
// 5. 设置初始化参数(可选)
Set<Entry<String, String>> initParameters =
getInitParameters().entrySet();
for (Entry<String, String> initParameter : initParameters) {
dynamic.setInitParameter(
initParameter.getKey(), initParameter.getValue());
}
}3.2 ServletContext.addServlet() 在 Tomcat 中的实现
java
// ApplicationContext.java (Tomcat)
@Override
public ServletRegistration.Dynamic addServlet(
String servletName, Servlet servlet) {
// 1. 检查名称是否唯一
if (wrapperMap.containsKey(servletName)) {
return null; // 已存在同名 Servlet
}
// 2. 创建 Wrapper(Tomcat 的 Servlet 包装器)
Wrapper wrapper = tomcatContext.createWrapper();
wrapper.setName(servletName);
wrapper.setServlet(servlet);
// 3. 添加到 Tomcat Context
tomcatContext.addChild(wrapper);
// 4. 返回注册对象
return new ApplicationServletRegistration(wrapper, tomcatContext);
}3.3 注册时序
TomcatServletWebServerFactory.getWebServer(initializers)
│
├─ new Tomcat()
│
├─ tomcat.getHost().createContext("/", docBase)
│ └─ StandardContext (Tomcat Context)
│
├─ for each ServletContextInitializer:
│ ├─ DispatcherServletRegistrationBean.onStartup()
│ │ └─ servletContext.addServlet("dispatcherServlet", dispatcherServlet)
│ │ └─ Wrapper 添加到 StandardContext
│ │
│ ├─ CharacterEncodingFilter
│ ├─ HiddenHttpMethodFilter
│ └─ FormContentFilter
│
├─ tomcat.start()
│ └─ StandardContext.start()
│ └─ Wrapper.load()
│ └─ DispatcherServlet.init() ← 实际初始化在首次请求时
│
└─ return TomcatWebServer(tomcat)4. addMappingForUrlPatterns() 的 URL 模式映射
4.1 源码
java
// ServletRegistrationBean.java
private void register(String description, ServletContext servletContext) {
Dynamic dynamic = servletContext.addServlet(...);
// 添加 URL 映射
dynamic.addMappingForUrlPatterns(
this.matchAlways, // true
!isAsyncSupported(), // true
getUrlMappings().toArray(new String[0])); // ["/"]
}
// DispatcherServletRegistrationBean 构造函数
public DispatcherServletRegistrationBean(
DispatcherServlet servlet,
DispatcherServletPath dispatcherServletPath) {
super(servlet, "/"); // ← 默认映射到根路径 "/"
this.dispatcherServletPath = dispatcherServletPath;
setName("dispatcherServlet");
}4.2 addMappingForUrlPatterns 的 3 个参数
java
// javax.servlet.ServletRegistration.Dynamic
public interface Dynamic extends ServletRegistration {
/**
* 添加 URL 映射模式
*
* @param urlPatterns 要映射的 URL 模式数组
* @param isMatchAfter 是否在已有映射之后匹配
* - true: 在默认 Servlet 之后匹配(可覆盖默认 Servlet)
* - false: 在默认 Servlet 之前匹配
*/
Set<String> addMappingForUrlPatterns(
String... urlPatterns);
// 或者使用带 matchAlways 的方法
Set<String> addMappingForUrlPatterns(
boolean matchAlways,
boolean matchAfter,
String... urlPatterns);
}4.3 为什么 DispatcherServlet 映射到 /
yaml
# DispatcherServlet 默认映射到 "/"
# 这意味着它是所有 HTTP 请求的默认入口
# 如果需要修改 DispatcherServlet 的路径
spring:
mvc:
servlet:
path: /api # 所有请求以 /api 开头才进入 DispatcherServlet
# 其他请求由内嵌容器处理(如静态资源)4.4 / 与 /* 的区别
| 映射 | 说明 | 效果 |
|---|---|---|
/ | 默认 Servlet 映射 | 匹配所有未被其他 Servlet 处理的请求,DispatcherServlet 会自行判断是否能处理 |
/* | 路径映射 | 匹配所有请求路径,包括 JSP 等,不会再去匹配其他 Servlet |
/api/* | 前缀映射 | 只匹配以 /api 开头的请求 |
java
// DispatcherServlet 使用 "/" 而不是 "/*" 的原因
// "/" 是 Servlet 规范中的"默认 Servlet"映射
// 它会让 DispatcherServlet 处理大多数请求,但静态资源仍可以由容器处理
//
// 如果使用 "/*",所有请求都进入 DispatcherServlet
// 包括 JSP 请求(如 /WEB-INF/views/index.jsp)
// 这会导致 JSP 无法正常渲染(DispatcherServlet 无法处理 JSP)5. DispatcherServletPath 接口
5.1 源码
java
// DispatcherServletPath.java
@FunctionalInterface
public interface DispatcherServletPath {
/**
* 获取 DispatcherServlet 的 URL 路径。
* 通常从 spring.mvc.servlet.path 读取。
*
* 默认值: "/"(所有请求)
* 可选值: "/api"、"/*.do" 等
*/
String getPath();
/**
* 获取路径前缀。
* 如果 path 是 "/api" → prefix 是 ""(空字符串)
* 如果 path 是 "/api/*" → prefix 是 "/api"
*/
default String getPrefix() {
String path = getPath();
int index = path.indexOf('*');
if (index >= 0) {
return path.substring(0, index);
}
return path;
}
}5.2 实现类
java
// DispatcherServletPath 的默认实现 —— 由 WebMvcProperties 提供
public class WebMvcProperties {
private final Servlet servlet = new Servlet();
public static class Servlet {
/**
* DispatcherServlet 的路径映射。
* 对应配置: spring.mvc.servlet.path
* 默认: "/"
*/
private String path = "/";
// getter / setter
}
// DispatcherServletPath 的适配器
public DispatcherServletPath getDispatcherServletPath() {
return () -> getServlet().getPath();
}
}5.3 在 DispatcherServletRegistrationBean 中的使用
java
// DispatcherServletAutoConfiguration.java
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties,
ObjectProvider<MultipartConfigElement> multipartConfig) {
// 创建注册 Bean,注入 DispatcherServletPath
DispatcherServletRegistrationBean registration =
new DispatcherServletRegistrationBean(
dispatcherServlet,
webMvcProperties.getDispatcherServletPath() // ← DispatcherServletPath
);
// 设置 Multipart 配置
registration.setMultipartConfig(
multipartConfig.getIfAvailable());
return registration;
}5.4 路径配置示例
yaml
# 默认: DispatcherServlet 处理所有请求
spring:
mvc:
servlet:
path: / # 所有请求进入 DispatcherServlet
# 修改: DispatcherServlet 只处理 /api 开头的请求
spring:
mvc:
servlet:
path: /api # 只处理 /api/xxx 的请求
# 静态资源(/static/css/*)由容器默认 Servlet 处理6. setLoadOnStartup(-1) 的延迟加载
6.1 源码
java
// ServletRegistrationBean.java
private int loadOnStartup = -1; // 默认 -1
@Override
protected void register(String description, ServletContext servletContext) {
Dynamic dynamic = servletContext.addServlet(getServletName(), this.servlet);
// 设置 load-on-startup
// -1 = 首次请求时初始化
// 0 = 在容器启动时初始化
// >0 = 按正数顺序在容器启动时初始化(值越小越先初始化)
dynamic.setLoadOnStartup(this.loadOnStartup);
}6.2 loadOnStartup 值的影响
| 值 | 初始化时机 | 说明 |
|---|---|---|
-1 | 首次请求时 | 延迟加载,加快启动速度(默认) |
0 | 容器启动时 | 应用启动时就初始化,但顺序由容器决定 |
1 | 容器启动时(高优先级) | 值越小越先初始化 |
10 | 容器启动时(低优先级) | 在有顺序要求的场景使用 |
6.3 为什么 DispatcherServlet 默认是 -1
java
// Spring Boot 选择 -1 的原因:
// 1. 加快启动速度
// - 不需要在启动时创建 DispatcherServlet 的 9 个 WebApplicationContext 组件
// - 包括 HandlerMapping、HandlerAdapter、ViewResolver 等
//
// 2. 首次请求时初始化
// - Tomcat 在第一次 HTTP 请求到达时调用 DispatcherServlet.init()
// - init() 中调用 initStrategies() 初始化所有 MVC 组件
//
// 3. 依赖的 Bean 已就绪
// - 当首次请求到达时,ApplicationContext 已经完全 refresh 完毕
// - 所有 Bean 都已创建完成
// DispatcherServlet.init() 中的初始化
@Override
protected void initStrategies(ApplicationContext context) {
// 初始化 9 个策略组件
initMultipartResolver(context); // 1. 文件上传解析器
initLocaleResolver(context); // 2. 本地化解析器
initThemeResolver(context); // 3. 主题解析器
initHandlerMappings(context); // 4. 处理器映射(核心)
initHandlerAdapters(context); // 5. 处理器适配器(核心)
initHandlerExceptionResolvers(context); // 6. 异常解析器
initRequestToViewNameTranslator(context); // 7. 请求到视图名翻译器
initViewResolvers(context); // 8. 视图解析器
initFlashMapManager(context); // 9. FlashMap 管理器
}6.4 如果需要立即初始化
java
// 自定义配置:在启动时就初始化 DispatcherServlet
@Component
public class StartupInitConfigurer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addContextCustomizers(context -> {
// 获取 DispatcherServlet 的 Wrapper
Wrapper wrapper = (Wrapper) context.findChild("dispatcherServlet");
if (wrapper != null) {
wrapper.setLoadOnStartup(0); // 启动时立即初始化
}
});
}
}7. WebMvcAutoConfiguration.EnableWebMvcConfiguration 的保护机制
7.1 源码
java
// WebMvcAutoConfiguration.java
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
// 核心:配置 DispatcherServlet 所需的 MVC 组件
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebMvcProperties.class)
public static class WebMvcAutoConfigurationAdapter
implements WebMvcConfigurer, ServletContextAware {
// 配置 HandlerMapping、HandlerAdapter、Converter 等
}
// 关键保护机制:有 @EnableWebMvc(WebMvcConfigurationSupport)时不加载
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public static class EnableWebMvcConfiguration {
// 用户自定义 WebMvcConfigurationSupport 时跳过
}
}
// 上层配置
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
public static class WebMvcAutoConfigurationAdapter {
// ... 自动配置的 MVC 组件
}7.2 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 的作用
java
// 默认:Spring Boot 自动配置 MVC 组件
// WebMvcAutoConfiguration 生效
// → 注册 HandlerMapping、HandlerAdapter、Converter、ViewResolver 等
// 手动:用户使用 @EnableWebMvc
@Configuration
@EnableWebMvc // ← @Import(DelegatingWebMvcConfiguration.class)
// → extends WebMvcConfigurationSupport
public class MyMvcConfig {
// 用户完全控制 MVC 配置
//
// 由于 DelegatingWebMvcConfiguration 继承自 WebMvcConfigurationSupport
// @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 不匹配
// → WebMvcAutoConfiguration 不生效
//
// 用户需要自己配置所有的 HandlerMapping、HandlerAdapter 等
}7.3 配置链对比
java
// 场景 1: 不使用 @EnableWebMvc(默认)
// WebMvcAutoConfiguration 生效
// 自动配置:
// - RequestMappingHandlerMapping
// - RequestMappingHandlerAdapter
// - DefaultAnnotationHandlerMapping
// - HttpMessageConverters (Jackson, StringHttpMessageConverter...)
// - ViewResolvers (InternalResourceViewResolver...)
// - Validator
// - ConversionService
// 场景 2: 使用 @EnableWebMvc
// DelegatingWebMvcConfiguration 生效(继承 WebMvcConfigurationSupport)
// WebMvcAutoConfiguration 跳过
// 用户通过下面的方式自定义:
@Configuration
@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 自定义消息转换器
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 自定义拦截器
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// 自定义视图解析器
}
}8. HiddenHttpMethodFilter / FormContentFilter 的注册
8.1 源码
java
// WebMvcAutoConfiguration.java
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
// 其他条件和配置...
public class WebMvcAutoConfiguration {
// HiddenHttpMethodFilter —— 支持 RESTful 的 PUT/DELETE
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
// 将 HTTP POST 转换为 PUT/DELETE/PATCH
// 通过 _method 参数指定实际方法
return new OrderedHiddenHttpMethodFilter();
}
// FormContentFilter —— 支持 non-POST 请求的表单参数
@Bean
@ConditionalOnMissingBean(FormContentFilter.class)
@ConditionalOnProperty(
prefix = "spring.mvc.formcontent.filter",
name = "enabled", matchIfMissing = true)
public OrderedFormContentFilter formContentFilter() {
// 将 PUT/PATCH/DELETE 请求的 application/x-www-form-urlencoded 内容
// 读取为 ServletRequest.getParameter() 可获取的参数
return new OrderedFormContentFilter();
}
}8.2 HiddenHttpMethodFilter 的工作原理
java
// HiddenHttpMethodFilter.java (Spring Web)
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
// 默认从 _method 参数读取实际方法
public static final String DEFAULT_METHOD_PARAM = "_method";
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
// 1. 只处理 POST 请求
if (!"POST".equals(request.getMethod())) {
chain.doFilter(request, response);
return;
}
// 2. 从 _method 参数获取实际方法
String method = request.getParameter(this.methodParam);
if (StringUtils.hasText(method)) {
// 3. 包装为 HttpMethodRequestWrapper
// 将 getMethod() 返回值改为指定的方法
HttpServletRequest wrapper =
new HttpMethodRequestWrapper(request, method.toUpperCase());
chain.doFilter(wrapper, response);
} else {
chain.doFilter(request, response);
}
}
}8.3 FormContentFilter 的工作原理
java
// FormContentFilter.java (Spring Web)
public class FormContentFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
// 1. 只处理 PUT/PATCH/DELETE 请求
if (!isFormContentType(request) || !isSupportedMethod(request)) {
chain.doFilter(request, response);
return;
}
// 2. 包装请求,将表单内容缓存
// 使 request.getParameter() 可以读取 PUT 请求的表单数据
FormContentRequestWrapper wrapper =
new FormContentRequestWrapper(request);
// 3. 继续过滤器链
chain.doFilter(wrapper, response);
}
}8.4 在 Servlet 容器中的注册顺序
java
// 注册顺序 (Filter 的顺序很重要)
OrderedHiddenHttpMethodFilter ← 1. 最先注册
OrderedFormContentFilter ← 2. 其次注册
// ... 其他 Filter
DispatcherServlet ← 最后注册(但 URL 映射不同)java
// Ordered 接口控制 Filter 顺序
public class OrderedHiddenHttpMethodFilter
extends HiddenHttpMethodFilter implements Ordered {
// 默认优先级 = REQUEST_WRAPPER_FILTER_MAX_ORDER - 10000
// REQUEST_WRAPPER_FILTER_MAX_ORDER = Integer.MAX_VALUE - 1
// → 优先级 ≈ Integer.MAX_VALUE - 10001 (非常低)
//
// 但这并不是说它的优先级低,而是 Spring Boot 的 Filter 排序策略:
// 值越小 → 越先执行
// 值越大 → 越后执行
// OrderedHiddenHttpMethodFilter 在 Ordered 接口中的排序值较低
// 因此它会在 DispatcherServlet 之前执行
}8.5 Filter 链的完整顺序
Web 请求到达
│
├─ 1. CharacterEncodingFilter(编码设置)
├─ 2. HiddenHttpMethodFilter(POST → PUT/DELETE)
├─ 3. FormContentFilter(PUT 表单参数读取)
├─ 4. OrderedRequestContextFilter(请求上下文)
│
└─ 5. DispatcherServlet(Spring MVC 入口)
│
├─ HandlerMapping(查找处理器)
├─ HandlerAdapter(执行处理器)
├─ HandlerInterceptor(拦截器链)
└─ ViewResolver(视图解析)8.6 常见配置
yaml
# 启用 HiddenHttpMethodFilter(支持 _method 参数)
spring:
mvc:
hiddenmethod:
filter:
enabled: true # 默认 false,需手动开启
# 启用 FormContentFilter(支持 PUT 表单参数)
spring:
mvc:
formcontent:
filter:
enabled: true # 默认 true总结
| # | 细节点 | 核心要点 |
|---|---|---|
| ① | @AutoConfigurationAfter 保证 | DispatcherServletAutoConfiguration 在 ServletWebServerFactoryAutoConfiguration 之后执行,确保 ServletContext 已就绪 |
| ② | 继承结构 | ServletContextInitializer → RegistrationBean → ServletRegistrationBean → DispatcherServletRegistrationBean |
| ③ | addRegistration() 容器注册 | servletContext.addServlet("dispatcherServlet", servlet) → 创建 Tomcat Wrapper 并添加到 StandardContext |
| ④ | URL 模式映射 | 默认映射到 "/"(默认 Servlet),可通过 spring.mvc.servlet.path 修改 |
| ⑤ | DispatcherServletPath | 从 spring.mvc.servlet.path 读取路径,影响 DispatcherServlet 的 URL 模式 |
| ⑥ | setLoadOnStartup(-1) | 默认首次请求时初始化,加快启动速度 |
| ⑦ | @ConditionalOnMissingBean 保护 | 用户使用 @EnableWebMvc(注册 WebMvcConfigurationSupport)时,自动配置跳过 |
| ⑧ | HiddenHttpMethodFilter / FormContentFilter | 在 DispatcherServlet 之前注册,支持 RESTful _method 参数和 PUT 表单参数读取 |