Springboot3 自动装配之核心文件:imports文件
注:本文以spring-boot v3.4.1源码为基础,梳理spring-boot应用启动流程、分析自动装配的原理
如果对spring-boot2自动装配有兴趣,可以看看我另一篇文章:
Springboot2 自动装配之spring-autoconfigure-metadata.properties和spring.factories(SPI机制核心)
启动入口
以下是源码里一段应用启动单元测试代码:
package org.springframework.boot.test.autoconfigure;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ConditionReportApplicationContextFailureProcessor}.
*
* @author Phillip Webb
* @author Scott Frederick
* @deprecated since 3.2.11 for removal in 3.6.0
*/
@ExtendWith(OutputCaptureExtension.class)
@Deprecated(since = "3.2.11", forRemoval = true)
@SuppressWarnings("removal")
class ConditionReportApplicationContextFailureProcessorTests {
@Test
void loadFailureShouldPrintReport(CapturedOutput output) {
SpringApplication application = new SpringApplication(TestConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
ConfigurableApplicationContext applicationContext = application.run();
ConditionReportApplicationContextFailureProcessor processor = new ConditionReportApplicationContextFailureProcessor();
processor.processLoadFailure(applicationContext, new IllegalStateException());
assertThat(output).contains("CONDITIONS EVALUATION REPORT")
.contains("Positive matches")
.contains("Negative matches");
}
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration(JacksonAutoConfiguration.class)
static class TestConfig {
}
}
spring-boot3应用启动入口是SpringApplication的构造方法,这个构造方法里做了一些初始化,比较重要。如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// @A
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// @B
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
// @C
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
@A:标签当前应用的启动主类,也就是我们平常写的xxxApplication类
@B:在类路径下查找是否有 :
- private static final String WEBMVC_INDICATOR_CLASS = “org.springframework.web.servlet.DispatcherServlet”;
- private static final String WEBFLUX_INDICATOR_CLASS = “org.springframework.web.reactive.DispatcherHandler”;
- private static final String JERSEY_INDICATOR_CLASS = “org.glassfish.jersey.servlet.ServletContainer”;
中的一个,标记当前web应用类型;web应用类型有:REACTIVE SERVLET NONE
@C:从类路径中可见的 spring.factories 文件中获取配置的BootstrapRegistryInitializer.class、ApplicationContextInitializer.class、ApplicationListener.class并缓存
启动核心方法 public ConfigurableApplicationContext run(String… args){}
public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.create();
if (this.properties.isRegisterShutdownHook()) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
// @A
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// @B
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();
if (this.properties.isLogStartupInfo()) {
new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
return context;
}
@A:创建DefaultBootstrapContext对象,逐个执行前面缓存中bootstrapRegistryInitializers的initialize方法
@B:从类路径META-INF/spring.factories配置文件中获取SpringApplicationRunListener配置的类(框架默认提供了EventPublishingRunListener)封装成SpringApplicationRunListeners,然后执行SpringApplicationRunListeners的starting方法,最终调用的是EventPublishingRunListener等配置类的starting方法
todo~~