注:本文以spring-boot v3.4.1源码为基础,梳理spring-boot应用启动流程、分析自动装配的原理
如果对spring-boot2自动装配有兴趣,可以看看我另一篇文章:
Springboot2 自动装配之spring-autoconfigure-metadata.properties和spring.factories(SPI机制核心)
1、启动入口
以下是源码里一段应用启动单元测试代码:
package org.springframework.boot.test.autoconfigure;
...
/**
* 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并缓存
2、启动核心方法 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 {
// @C
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
// @D
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// @E
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// @F
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方法
- @C:创建ConfigurableEnvironment ,代码参看下面【第3章节】的prepareEnvironment方法
- @D:创建ConfigurableApplicationContext对象,实现具体代码是:org.springframework.boot.DefaultApplicationContextFactory#create,默认情况下是返回的new AnnotationConfigApplicationContext() 这个对象
- @E:准备各种Context,详情见下面代码【第4章节】的prepareContext方法
- @F:刷新容器,这一块逻辑主要是执行spring-framework原生的refresh方法;这个核心方法主要做的内容就是解析bean定义,然后执行整个bean生命周期;具体流程可以看我的另一篇文章:
3、prepareEnvironment方法:创建ConfigurableEnvironment
接【第2章节】 @C 代码:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// @A
ConfigurableEnvironment environment = getOrCreateEnvironment();
// @B
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// @C
listeners.environmentPrepared(bootstrapContext, environment);
ApplicationInfoPropertySource.moveToEnd(environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
跟踪了一下代码:
- @A:方法里执行了 new ApplicationEnvironment()创建对象并返回了,在构造方法里会触发StandardEnvironment#customizePropertySources方法,这个方法会把System.getProperties()的值放到环境的systemProperties环境值,然后把ProcessEnvironment.getenv()的值放到systemEnvironment环境值
- @B:配置spring应用环境信息,解析启动命令里的参数
- 创建一个ApplicationConversionService对象赋值到 ApplicationEnvironment的ConversionService属性;ApplicationConversionService主要作用是提供类型转换服务,可以将A类型数据转换为B类型数据。 细节可以参看这里: ConversionService介绍
- 如果在启动参数中加了commandLineArgs参数,并且属性源(PropertySource)有commandLineArgs这个名称,则会在Environment里进行真实PropertySource替换;(这一块没太看懂具体要做的目的)
- 最后创建一个ApplicationInfoPropertySource对象到ApplicationEnvironment的PropertySource链表中
- @C:执行在【第2章节】创建的SpringApplicationRunListeners的environmentPrepared方法
4、prepareContext方法:准备各种Context,拉齐属性
接【第2章节】,DefaultBootstrapContext 是spring-boot的类,而 ConfigurableApplicationContext是spring-frampwork的类;这个方法会把两个类关联起来
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// @A
context.setEnvironment(environment);
// @B
postProcessApplicationContext(context);
addAotGeneratedInitializerIfNecessary(this.initializers);
// @C
applyInitializers(context);
// @D
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.properties.isLogStartupInfo()) {
logStartupInfo(context.getParent() == null);
logStartupInfo(context);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// @E
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences());
if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding());
}
}
if (this.properties.isLazyInitialization()) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
if (this.properties.isKeepAlive()) {
context.addApplicationListener(new KeepAlive());
}
// @F
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
if (!AotDetector.useGeneratedArtifacts()) {
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
}
// @G
listeners.contextLoaded(context);
}
- @A:将刚刚创建的环境信息对象,设置到spring context里(即AnnotationConfigApplicationContext对象),使其和spring-boot的环境信息ConfigurableEnvironment对象保持一致
- @B:这里继续对齐两个Context的属性,包括:
- 1、向spring context注入bean定义:name为org.springframework.context.annotation.internalConfigurationBeanNameGenerator
- 2、设置spring context的ResourceLoader和ClassLoader属性
- 3、对齐sping容器属性:ConversionService conversionService,这个对象是在【第3章节】@B时机创建的一个ApplicationConversionService对象
- @C:获取所有的ApplicationContextInitializer对象,并执行initialize方法;在initialize方法可以向ConfigurableListableBeanFactory bean工厂里注入一些自定义的bean定义或者其他bean工厂处理
- @D:执行所有SpringApplicationRunListener子类的contextPrepared方法,框架默认提供的SpringApplicationRunListener子类是EventPublishingRunListener,是在【第2章节】@B位置创建的对象;SpringApplicationRunListeners实则是对SpringApplicationRunListener集合的封装,两者相差一个字母 s
- @E:拿到beanFactory,一顿自定义操作
- @F:注册BeanFactory后置处理器
- @G:调用所有SpringApplicationRunListener子类的 contextLoaded方法,通知context已加载完毕
建议
在看源码的时候有一些关键类需要注意一下DefaultBootstrapContext bootstrapContext是springboot提供的, ConfigurableApplicationContext context是spring-framework 提供的,这样就会更清晰一点;
todo~~