以 DispatcherServlet 为例的源码级深度对比
先看 DispatcherServlet 初始化时需要哪些组件(源码 spring-webmvc/DispatcherServlet.java:441):
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); // 1. 文件上传解析器
initLocaleResolver(context); // 2. 国际化解析器
initHandlerMappings(context); // 3. 处理器映射器(URL → Controller)
initHandlerAdapters(context); // 4. 处理器适配器(调用Controller方法)
initHandlerExceptionResolvers(context); // 5. 异常解析器
initRequestToViewNameTranslator(context);// 6. 视图名称翻译器
initViewResolvers(context); // 7. 视图解析器
initFlashMapManager(context); // 8. Flash属性管理器
}
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context:component-scan base-package="com.example.controller"/>
<mvc:annotation-driven/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<!-- 静态资源 -->
<mvc:resources mapping="/static/**" location="/static/"/>
<!-- 国际化 -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="zh_CN"/>
</bean>
<!-- 异常解析 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"/>
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 还没完...还有 Jackson消息转换器、跨域配置、字符编码Filter等 -->
Spring Boot 用户只需要:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
# application.yml(只写差异化配置)
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: "123456"
1@SpringBootApplication 拆开看
// 源码:spring-boot-autoconfigure/SpringBootApplication.java
@SpringBootConfiguration // 就是 @Configuration
@EnableAutoConfiguration // ← 核心!开启自动配置
@ComponentScan( // 组件扫描
excludeFilters = {
@Filter(type = CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication { ... }
2@EnableAutoConfiguration 导入选择器
// 源码:EnableAutoConfiguration.java
@AutoConfigurationPackage // 自动注册主类所在包
@Import(AutoConfigurationImportSelector.class) // ← 关键!
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
3AutoConfigurationImportSelector 5步流水线
// 源码:AutoConfigurationImportSelector.java
protected AutoConfigurationEntry getAutoConfigurationEntry(...) {
// ① 获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// ② 【核心】从 .imports 文件加载所有候选配置类
List<String> configurations = getCandidateConfigurations(...);
// ③ 去重
configurations = removeDuplicates(configurations);
// ④ 排除 exclude 列表
Set<String> exclusions = getExclusions(...);
configurations.removeAll(exclusions);
// ⑤ 【核心】条件过滤!@ConditionalOnClass / @ConditionalOnMissingBean
configurations = getConfigurationClassFilter().filter(configurations);
// ⑥ 发送事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
4ImportCandidates 读取 .imports 文件
// 源码:ImportCandidates.java
private static final String LOCATION = "META-INF/spring/%s.imports";
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
String location = String.format(LOCATION, annotation.getName());
// 实际读取:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url)); // 逐行读取类名
}
return new ImportCandidates(importCandidates);
}
spring.factories,2.7+ 迁移到 .imports,3.x 完全使用 .imports。
5OnClassCondition 多线程高效过滤
// 源码:OnClassCondition.java
class OnClassCondition extends FilteringSpringBootCondition {
protected ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, ...) {
// 多核CPU时拆成两半并行处理,提高效率!
if (autoConfigurationClasses.length > 1
&& Runtime.getRuntime().availableProcessors() > 1) {
return resolveOutcomesThreaded(autoConfigurationClasses, ...);
}
}
private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
// 只检查 classpath 是否存在该类,不真正加载!
if (ClassNameFilter.MISSING.matches(className, classLoader)) {
return ConditionOutcome.noMatch(...); // 类不存在 → 不生效
}
return null; // 类存在 → 候选通过
}
}
// 源码:spring-boot-webmvc/DispatcherServletAutoConfiguration.java
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // ← 最高优先级
@AutoConfiguration // ← 标记为自动配置类
@ConditionalOnWebApplication(type = Type.SERVLET) // ← 条件1:必须是Servlet Web应用
@ConditionalOnClass(DispatcherServlet.class) // ← 条件2:classpath必须有DispatcherServlet
public final class DispatcherServletAutoConfiguration {
// ===== 内部配置类1:创建 DispatcherServlet =====
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class) // ← 条件3:用户没自定义才生效!
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class) // ← 绑定 spring.mvc.* 配置
protected static class DispatcherServletConfiguration {
@Bean(name = "dispatcherServlet")
DispatcherServlet dispatcherServlet(WebMvcProperties props) {
DispatcherServlet ds = new DispatcherServlet();
// 从 application.yml 的 spring.mvc.* 读取,替代XML中的 <property>
ds.setDispatchOptionsRequest(props.isDispatchOptionsRequest());
ds.setDispatchTraceRequest(props.isDispatchTraceRequest());
ds.setPublishEvents(props.isPublishRequestHandledEvents());
ds.setEnableLoggingRequestDetails(props.isLogRequestDetails());
return ds;
}
// 兼容性Bean:用户可能命名错误的multipartResolver
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = "multipartResolver") // ← 容器没有才注册!
MultipartResolver multipartResolver(MultipartResolver resolver) {
return resolver; // 自动重命名为标准名
}
}
// ===== 内部配置类2:注册 DispatcherServlet 到Servlet容器 =====
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class) // ← 依赖上面的配置类
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = "dispatcherServletRegistration")
@ConditionalOnBean(value = DispatcherServlet.class, name = "dispatcherServlet")
DispatcherServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet ds, WebMvcProperties props,
ObjectProvider<MultipartConfigElement> multipartConfig) {
// 自动注册Servlet映射路径(默认"/")—— 替代 web.xml 中的 <servlet-mapping>
DispatcherServletRegistrationBean registration =
new DispatcherServletRegistrationBean(ds, props.getServlet().getPath());
registration.setName("dispatcherServlet");
registration.setLoadOnStartup(props.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
// ===== 自定义条件判断:用户是否已定义DispatcherServlet =====
private static final class DefaultDispatcherServletCondition extends SpringBootCondition {
public ConditionOutcome getMatchOutcome(ConditionContext context, ...) {
List<String> beans = Arrays.asList(
beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
// 用户已经定义了名为 "dispatcherServlet" 的Bean → 不匹配(用户优先!)
if (beans.contains("dispatcherServlet")) {
return ConditionOutcome.noMatch(...);
}
// 容器中没有任何DispatcherServlet → 匹配(自动配置补上)
if (beans.isEmpty()) {
return ConditionOutcome.match(...);
}
return ConditionOutcome.match(...);
}
}
}
| 配置项 | 原生 Spring | Spring Boot |
|---|---|---|
| Servlet 声明 | web.xml 中 <servlet> + <servlet-mapping>(8行) | 自动创建+注册,@ConditionalOnMissingBean |
| Servlet 路径 | <url-pattern>/</url-pattern> | spring.mvc.servlet.path(默认 /) |
| 启动加载 | <load-on-startup>1</load-on-startup> | spring.mvc.servlet.load-on-startup |
| 文件上传 | 手动配 MultipartResolver(6行) | 自动配 + 兼容命名错误 |
| 视图解析器 | 手动配 InternalResourceViewResolver | 自动配(默认适合REST) |
| 静态资源 | <mvc:resources> | 自动映射 classpath:/static/ |
| 字符编码 | 手动配 CharacterEncodingFilter | 自动配 UTF-8 |
| Jackson | 手动配 MappingJackson2HttpMessageConverter | classpath有就自动配 |
| 数据源 | 手动8行+连接池参数 | 3行YAML,自动选HikariCP |
| 事务管理器 | 手动配 + <tx:annotation-driven/> | 有DataSource就自动配 |
| 注解 | 作用 | 源码条件类 |
|---|---|---|
@ConditionalOnClass | classpath 有该类时生效 | OnClassCondition |
@ConditionalOnMissingClass | classpath 没有该类时生效 | OnClassCondition |
@ConditionalOnBean | 容器中有该 Bean 时生效 | OnBeanCondition |
@ConditionalOnMissingBean | 容器中没有该 Bean 时生效 | OnBeanCondition |
@ConditionalOnProperty | 配置属性满足条件时生效 | OnPropertyCondition |
@ConditionalOnWebApplication | 是 Web 应用时生效 | OnWebApplicationCondition |
@ConditionalOnExpression | SpEL 表达式为 true 时生效 | OnExpressionCondition |
@Import(AutoConfigurationImportSelector) 从 .imports 文件加载候选配置类,再由 @ConditionalOnClass、@ConditionalOnMissingBean 过滤。以 DispatcherServlet 为例:@ConditionalOnWebApplication 保证非Web不加载,@ConditionalOnClass 保证classpath没有时不报错,DefaultDispatcherServletCondition 保证用户自定义了就退让。"
ServletRegistrationBean 以编程方式注册 DispatcherServlet 并映射路径。原来 web.xml 的 <servlet>、<servlet-mapping> 等,现在由自动配置 + @EnableConfigurationProperties(WebMvcProperties.class) 绑定 YAML 完成。"
@ConditionalOnMissingBean,容器已有同类型/同名 Bean 就跳过。比如用户自定义了 DispatcherServlet,DefaultDispatcherServletCondition 会返回 noMatch,自动配置就不创建。"
ClassNameFilter.MISSING.matches(className, classLoader) 来检测。它只检查类是否存在,不真正加载类,避免触发类初始化。多核CPU时还会将候选配置类拆成两半并行过滤,提升启动速度。"