拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Spring Boot DispatcherServlet 自动配置源码深度解读

版本说明本文基于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(afterServletWebServerFactoryAutoConfiguration.class)ConditionalOnWebApplication(typeType.SERVLET)ConditionalOnClass(DispatcherServlet.class)publicclassDispatcherServletAutoConfiguration{// ...}2.1 核心注解解读注解作用AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)指定该自动配置类拥有最高优先级优先于其他自动配置执行AutoConfiguration(after ServletWebServerFactoryAutoConfiguration.class)在 Web 服务器工厂自动配置之后执行确保 Servlet 容器已就绪ConditionalOnWebApplication(type Type.SERVLET)仅在Servlet 类型的 Web 应用中生效区别于 Reactive WebConditionalOnClass(DispatcherServlet.class)仅当类路径中存在DispatcherServlet时才生效2.2 两个关键常量publicstaticfinalStringDEFAULT_DISPATCHER_SERVLET_BEAN_NAMEdispatcherServlet;publicstaticfinalStringDEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAMEdispatcherServletRegistration;DEFAULT_DISPATCHER_SERVLET_BEAN_NAMEDispatcherServlet实例在 Spring 容器中的 Bean 名称默认为dispatcherServlet。DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAMEServletRegistrationBean在 Spring 容器中的 Bean 名称用于向 Servlet 容器注册DispatcherServlet。三、DispatcherServletConfiguration — 创建 DispatcherServlet 实例DispatcherServletAutoConfiguration内部定义了第一个静态内部配置类DispatcherServletConfiguration负责创建DispatcherServlet的 Bean 实例。Configuration(proxyBeanMethodsfalse)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(nameDEFAULT_DISPATCHER_SERVLET_BEAN_NAME)publicDispatcherServletdispatcherServlet(WebMvcPropertieswebMvcProperties){DispatcherServletdispatcherServletnewDispatcherServlet();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() 方法BeanConditionalOnBean(MultipartResolver.class)ConditionalOnMissingBean(nameDispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)publicMultipartResolvermultipartResolver(MultipartResolverresolver){returnresolver;}该方法的作用是检测并适配用户自定义的MultipartResolverConditionalOnBean(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而非ObjectProviderMultipartResolver。这意味着当容器中存在多个MultipartResolver类型的 Bean 时Spring 在注入方法参数时可能抛出NoUniqueBeanDefinitionException。ConditionalOnBean(MultipartResolver.class)仅检查至少存在一个并未限制恰好存在一个。因此如果用户自定义了多个MultipartResolverBean需要配合Primary或Qualifier指定优先级以避免注入冲突。四、DispatcherServletRegistrationConfiguration — 注册 DispatcherServlet第二个内部静态配置类DispatcherServletRegistrationConfiguration负责将DispatcherServlet注册到 Servlet 容器中。Configuration(proxyBeanMethodsfalse)Conditional(DispatcherServletRegistrationCondition.class)ConditionalOnClass(ServletRegistration.class)EnableConfigurationProperties(WebMvcProperties.class)Import(DispatcherServletConfiguration.class)protectedstaticclassDispatcherServletRegistrationConfiguration{// ...}注意Import(DispatcherServletConfiguration.class)这意味着注册配置会导入创建配置确保DispatcherServlet实例优先于注册 Bean 被创建。4.1 dispatcherServletRegistration() 方法Bean(nameDEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)ConditionalOnBean(valueDispatcherServlet.class,nameDEFAULT_DISPATCHER_SERVLET_BEAN_NAME)publicDispatcherServletRegistrationBeandispatcherServletRegistration(DispatcherServletdispatcherServlet,WebMvcPropertieswebMvcProperties,ObjectProviderMultipartConfigElementmultipartConfig){DispatcherServletRegistrationBeanregistrationnewDispatcherServletRegistrationBean(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 DefaultDispatcherServletConditionOrder(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-webDispatcherServletAutoConfiguration便会自动创建并注册DispatcherServlet映射到根路径/开发者无需任何额外配置即可开始 Web 开发。6.2 条件化控制尊重用户配置通过精心设计的条件注解和自定义条件类Spring Boot 实现了“用户自定义优先”的策略如果用户已经创建了DispatcherServlet自动配置不会覆盖如果用户已经注册了ServletRegistrationBean自动配置不会重复注册。6.3 职责分离高内聚低耦合将创建实例和注册到容器拆分为两个独立的配置类和两个独立的 Bean使得每个步骤都可以被独立地条件化控制也便于用户进行部分覆盖。6.4 配置属性驱动灵活可调通过EnableConfigurationProperties(WebMvcProperties.class)将spring.mvc.*配置项与DispatcherServlet的行为参数绑定开发者可以通过配置文件灵活调整DispatcherServlet的各项行为无需修改代码。DispatcherServletAutoConfiguration是 Spring Boot 自动配置哲学的典范之作——它用简洁优雅的代码实现了复杂而灵活的条件化自动装配让开发者真正享受到开箱即用的便捷体验。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门