2.SpringBoot框架启动流程之Spi机制

在 Java 应用中,类路径(Classpath) 包括:

  • 编译后的 .class 文件目录(如 target/classes)。
  • 所有依赖 JAR 包的根目录。

其中,前者是类ClassPathScanningCandidateComponentProvider主动扫描注解ComponentScan指定的路径,后者是类DeferredImportSelector按需加载。注解ComponentScan指定的路径通常是应用目录,当然也可以指定第三方jar包下的目录。

DeferredImportSelector加载的类主要包括:

  • @Import 注解导入的外部类。当然包括应用中显式导入、第三方jar包中显式导入的候选类。
  • 定义文件:org.springframework.boot.autoconfigure.AutoConfiguration.imports,并且该文件位于路径META-INF/spring/...之下。当然包括应用路径src/main/resources/META-INF/spring下指定、第三方jar中指定。

当前文章分析的核心是:解析启动类@Import时获取得到的 AutoConfigurationImportSelector

DeferredImportSelector:A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans have been processed. This type of selector can be particularly useful when the selected imports are {@code @Conditional}。


1、DeferredImportSelectorHandler

private class DeferredImportSelectorHandler {

	@Nullable
	private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

	public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
		DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
		if (this.deferredImportSelectors == null) {
			DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
			handler.register(holder);
			handler.processGroupImports();
		}
		else {
			this.deferredImportSelectors.add(holder);
		}
	}

	public void process() {
		List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
		this.deferredImportSelectors = null;
		try {
			if (deferredImports != null) {
				DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
				deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
				deferredImports.forEach(handler::register);
				handler.processGroupImports();
			}
		}
		finally {
			this.deferredImportSelectors = new ArrayList<>();
		}
	}
}

deferredImportSelectors集合中元素: 是解析启动类的@Import获取到ImportSelector类型的AutoConfigurationImportSelector时通过DeferredImportSelectorHandler#handle方法得到的。


2、DeferredImportSelectorGroupingHandler 

private class DeferredImportSelectorGroupingHandler {

	private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

	private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();

	public void register(DeferredImportSelectorHolder deferredImport) {
		Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
		DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
				(group != null ? group : deferredImport),
				key -> new DeferredImportSelectorGrouping(createGroup(group)));
		grouping.add(deferredImport);
		this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
				deferredImport.getConfigurationClass());
	}

	public void processGroupImports() {
		for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
			Predicate<String> exclusionFilter = grouping.getCandidateFilter();
			grouping.getImports().forEach(entry -> {
				ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
				processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
							Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
							exclusionFilter, false);
			});
		}
	}

	private Group createGroup(@Nullable Class<? extends Group> type) {
		Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
		return ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
				ConfigurationClassParser.this.environment,
				ConfigurationClassParser.this.resourceLoader,
				ConfigurationClassParser.this.registry);
	}
}
grouping.getImports():获取自动装配的类。
entry:遍历每个自动装配的类

3、DeferredImportSelectorGrouping

private static class DeferredImportSelectorGrouping {

	private final DeferredImportSelector.Group group;

	private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();

	DeferredImportSelectorGrouping(Group group) {
		this.group = group;
	}

	public void add(DeferredImportSelectorHolder deferredImport) {
		this.deferredImports.add(deferredImport);
	}
	
	public Iterable<Group.Entry> getImports() {
		for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
			this.group.process(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getImportSelector());
		}
		return this.group.selectImports();
	}

	public Predicate<String> getCandidateFilter() {
		Predicate<String> mergedFilter = DEFAULT_EXCLUSION_FILTER;
		for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
			Predicate<String> selectorFilter = deferredImport.getImportSelector().getExclusionFilter();
			if (selectorFilter != null) {
				mergedFilter = mergedFilter.or(selectorFilter);
			}
		}
		return mergedFilter;
	}
}

group.process:调用AutoConfigurationImportSelector的静态内部类AutoConfigurationGroup的方法AutoConfigurationGroup#process获取Spring.factories中涉及的类信息。

this.group.selectImports():对候选的自动装配类进行排序。首先按照全限定性名,其次再根据@AutoConfigureOrder、最后@AutoConfigureBefore @AutoConfigureAfter依次控制集合中候选类的顺序。

4、AutoConfigurationImportSelector

public class AutoConfigurationImportSelector implements DeferredImportSelector{
	
	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {
		AnnotationAttributes attributes = getAttributes(annotationMetadata);

		// 通过spi机制加载spring.factories文件下的目标类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//#1
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);

		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

	...

	private static class AutoConfigurationGroup implements DeferredImportSelector.Group{

		@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {

			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);

			this.autoConfigurationEntries.add(autoConfigurationEntry);

			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

		@Override
		public Iterable<Entry> selectImports() {
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			processedConfigurations.removeAll(allExclusions);
			// 对自动装配的候选类排序处理:根据order字段属性排序、根据注解@AutoConfigureAfter、@AutoConfigureBefore
			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}

		private List<String> sortAutoConfigurations(Set<String> configurations,
				AutoConfigurationMetadata autoConfigurationMetadata) {
			return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
                    //首先根据字母排序、其次根据Order排序、最后根据注解@AutoConfigureAfter、@AutoConfigureBefore 重新对候选类排序,改变其在集合中的顺序,从而影响候选类实例化顺序
					.getInPriorityOrder(configurations);
		}

	}
}

步骤1:通过spi机制加载spring.factories文件下的目标类。

步骤2:在spring.factories文件中获取 AutoConfigurationImportFilter类型的过滤类。如下所示:

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

步骤3:遍历所有候选类。利用AutoConfigurationImportFilter三种不同类型判断候选类是否满足当前条件。

5.spring.factories的历史

Spring Boot 2.4+版本之后spring.factories已经被.imports 文件取代:

特性spring.factories.imports 文件(新)
引入版本Spring Boot 1.x / 2.xSpring Boot 2.4+
文件路径META-INF/spring.factoriesMETA-INF/spring/autoconfiguration.imports
是否支持模块化❌ 差(递归加载问题)✅ 好(扁平、明确)
是否推荐⚠️ 旧方式,逐渐淘汰官方推荐新方式
兼容性✅ 仍支持✅ 新旧可共存
<think>嗯,用户让我介绍一下Spring Boot的自动配置和启动流程的核心源码理解。首先,我需要确认用户的需求是什么。他们可能正在学习Spring Boot,想要深入了解其内部工作原理,特别是自动配置和启动过程。用户可能已经有一些基础,但需要更详细的源码层面的解释,而不仅仅是概念。 接下来,我要考虑从哪里开始。自动配置是Spring Boot的核心特性之一,它基于条件注解来配置Bean。我需要回忆一下相关的核心类,比如@EnableAutoConfiguration,AutoConfigurationImportSelector,以及spring.factories文件的作用。这部分需要解释自动配置是如何被触发和加载的。 然后是启动流程,这部分涉及到SpringApplication类的run方法。用户可能想知道从启动到应用完全运行的过程中,各个阶段都做了什么。比如准备环境、创建应用上下文、加载Bean定义,最后启动Tomcat等嵌入式服务器。需要分解每个步骤,并提到关键的方法,比如refreshContext和onRefresh。 用户可能需要具体的例子或者源码片段来说明这些过程。例如,在自动配置中,如何通过spring.factories加载配置类,条件注解如@ConditionalOnClass是如何起作用的。在启动流程中,SpringApplication.run()中的各个步骤,比如prepareEnvironment、createApplicationContext、refreshContext等。 另外,可能需要提到一些设计模式,比如Spring Boot使用的工厂加载机制Spring Factories Loader),以及事件驱动模型(Application Events),这些在启动过程中用于处理不同阶段的事件通知。 还需要注意用户的潜在问题,比如自动配置的覆盖和自定义配置的方法,或者启动过程中的扩展点。例如,如何通过排除某些自动配置类,或者使用@SpringBootApplication注解的exclude属性。启动流程中的ApplicationRunner和CommandLineRunner接口,可以在应用启动后执行自定义代码。 最后,要确保解释清晰,结构合理,先讲自动配置,再讲启动流程,每个部分分点说明,并给出源码示例或关键类和方法。避免过于深入细节,但又要覆盖主要的核心部分,帮助用户建立整体的理解框架。</think>Spring Boot 的自动配置和启动流程是其核心设计理念的体现,以下从源码层面对其核心机制进行拆解: ### 一、自动配置核心源码解析 **1. 触发入口** `@SpringBootApplication` 组合了 `@EnableAutoConfiguration`,其核心逻辑由 `AutoConfigurationImportSelector` 实现。该类通过 `selectImports()` 方法加载配置: ```java // AutoConfigurationImportSelector.java public String[] selectImports(AnnotationMetadata metadata) { List<String> configurations = getCandidateConfigurations(metadata, attributes); configurations = removeDuplicates(configurations); configurations = filter(configurations, autoConfigurationMetadata); return StringUtils.toStringArray(configurations); } ``` **2. 配置加载机制** 通过 `SpringFactoriesLoader` 加载 `META-INF/spring.factories` 文件中的预定义配置类: ```properties # spring-boot-autoconfigure-2.x.jar 中 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,... ``` **3. 条件化装配** 通过 `@Conditional` 系列注解实现智能装配,典型实现类: ```java // OnClassCondition 检查类路径是否存在目标类 class OnClassCondition extends FilteringSpringBootCondition { // 通过 ClassLoader 加载检查 } ``` **4. 配置优先级控制** 通过 `@AutoConfigureOrder` 和 `@AutoConfigureAfter` 控制配置类加载顺序 --- ### 二、启动流程源码解析 **1. 入口方法** `SpringApplication.run()` 方法核心流程: ```java public ConfigurableApplicationContext run(String... args) { // 1. 初始化监听器 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); // 2. 准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 3. 创建应用上下文(区分 Web/非Web 环境) context = createApplicationContext(); // 4. 刷新上下文(IoC 容器初始化核心) refreshContext(context); // 5. 执行 Runner 接口实现 callRunners(context, applicationArguments); } ``` **2. 上下文刷新关键** `AbstractApplicationContext.refresh()` 方法包含 12 个标准步骤,核心扩展点: ```java // SpringApplication.java 中的 refreshContext protected void refresh(ApplicationContext applicationContext) { ((AbstractApplicationContext) applicationContext).refresh(); } // 嵌入式容器启动在 onRefresh 阶段 protected void onRefresh() { createWebServer(); // 创建 Tomcat/Jetty 等实例 } ``` **3. 嵌入式容器启动** 以 Tomcat 为例: ```java // TomcatServletWebServerFactory.java public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); // 配置 Connector、Context 等组件 return new TomcatWebServer(tomcat); } ``` --- ### 三、关键设计模式 1. **工厂加载机制**:Spring Factories Loader 实现 SPI 扩展 2. **条件判断体系**:基于 ConditionEvaluator 的条件评估框架 3. **事件驱动模型**:通过 ApplicationEvent 实现启动过程状态广播 --- ### 四、调试建议 1. 在 `ConditionEvaluator#shouldSkip` 方法打断点,观察条件判断逻辑 2. 追踪 `ConfigurationClassParser#processConfigurationClass` 查看配置类解析过程 3. 监控 `SpringApplicationRunListeners` 各事件触发节点 理解这些源码需要结合具体场景分析,建议通过实际调试跟踪执行流程,同时参考官方文档中的自动配置报告(`--debug` 模式输出)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值