SpringApplication类分析

SpringBoot项目通常的启动手段

1
2
3
4
5
6
7
8
@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

SpringApplication的run方法

SpringBootApplication中有两种重载的静态run方法。

其中一个方法返回值是调用另一个方法,另一个方法会返回一个ApplicationContext。

1
2
3
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}

在这里,调用了SpringApplication的构造器,传入了primarySources,就是Application.class,随后调用了run方法

SpringApplicaiton的构造方法

SpringApplication有两个构造方法,一个支持传入ResourceLoader,另一个则调用前一个方法,但设置ResourceLoader为null。

1
2
3
4
5
6
7
8
9
10
11
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
配置resourceLoader

首先配置resourceLoader

1
this.resourceLoader = resourceLoader;
配置primarySources

使用断言机制,用LinkedHashSet存储primarySources

1
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
配置WebApplicationType

配置properties中的WebApplicationType(共有三种:None、SERVLET和REACTIVE)

deduceFromClasspath()通过使用ClassUtils根据是否存在相应的类,依次判断,是否为REACTIVE,是否为None,最后默认返回SERVLET

1
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
配置bootstrap

配置bootstrap,调用getSpringFactoriesInstances方法,该方法会调用springframework中的SpringFactoriesLoader类

1
this.bootstrapRegistryInitializers = new ArrayList<>(				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

getSpringFactoriesInstances方法的终极实现如下

1
2
3
private <T> List<T> getSpringFactoriesInstances(Class<T> type, ArgumentResolver argumentResolver) {
return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver);
}
forDefaultResourceLocation方法

首先,SpringFactories类有一个forDefaultResourceLocation方法,FACTORIES_RESOURCE_LOCATION变量存储了factories的位置,也就是meta-inf下的spring.factories

1
2
3
4
5
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);
}

经过一系列调用,看到了真正的forResourceLocation方法。

1
2
3
4
5
6
7
8
9
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
ClassLoader resourceClassLoader = (classLoader != null ? classLoader :
SpringFactoriesLoader.class.getClassLoader());
Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(
resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());
return loaders.computeIfAbsent(resourceLocation, key ->
new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}

首先使用断言机制检查resourceLocation为Text

1
Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");

获取SpringFactoriesLoader类的classloader

1
2
ClassLoader resourceClassLoader = (classLoader != null ? classLoader :
SpringFactoriesLoader.class.getClassLoader());

可以看到在SpringFactoriesLoader类内还有一个简单的基于ConcurrentReferenceHashMap类的cache,存储了加载过得SpringFactoriesLoader

总而言之,会想办法返回一个SpringFactoriesLoader

1
new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation))

loadFactoriesResource这个方法,会遍历spring.factories这个方法,返回一个map,就是读入spring.factories这个文件,读取这些默认工厂。

load方法

load方法会加载并实例化在spring.factories指出的应使用的实现类,并使用AnnotationAwareOrderComparator排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,
@Nullable FailureHandler failureHandler) {
Assert.notNull(factoryType, "'factoryType' must not be null");
List<String> implementationNames = loadFactoryNames(factoryType);
logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));
List<T> result = new ArrayList<>(implementationNames.size());
FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER;
for (String implementationName : implementationNames) {
T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
if (factory != null) {
result.add(factory);
}
}
AnnotationAwareOrderComparator.sort(result);
return result;
}

instantiateFactory方法,就是先用ClassUtils获取Class实例,然后用FactoryInstantiator获取实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Nullable
protected <T> T instantiateFactory(String implementationName, Class<T> type,
@Nullable ArgumentResolver argumentResolver, FailureHandler failureHandler) {

try {
Class<?> factoryImplementationClass = ClassUtils.forName(implementationName, this.classLoader);
Assert.isTrue(type.isAssignableFrom(factoryImplementationClass), () ->
"Class [%s] is not assignable to factory type [%s]".formatted(implementationName, type.getName()));
FactoryInstantiator<T> factoryInstantiator = FactoryInstantiator.forClass(factoryImplementationClass);
return factoryInstantiator.instantiate(argumentResolver);
}
catch (Throwable ex) {
failureHandler.handleFailure(type, implementationName, ex);
return null;
}
}

FactoryInstantiator是SpringFactoriesLoader的一个静态final内部类,用于初始化工厂实例,主要是使用里面的instantiate方法,该方法会使用KoelinDelegate以防止运行时对Kotlin的hard dependency,或者直接调用类的构造器初始化示例。

1
2
3
4
5
6
7
T instantiate(@Nullable ArgumentResolver argumentResolver) throws Exception {
Object[] args = resolveArgs(argumentResolver);
if (isKotlinType(this.constructor.getDeclaringClass())) {
return KotlinDelegate.instantiate(this.constructor, args);
}
return this.constructor.newInstance(args);
}
setInitializers

前面已经分析道,SpringFactoriesLoader类中有一个缓存,再次调用getSpringFactoriesInstances方法会直接获得之前加载的spring.factories缓存结果并创建实例。

1
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

该方法也就是把spring.factories中配置的initializer载入

setListeners
1
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

同上,也就是把spring.factories中配置的listener载入

配置mainApplicationClass
1
this.mainApplicationClass = deduceMainApplicationClass();

deduceMainApplicationClass方法,调用了java.lang的stackWalker,目的是获取当前的主类。

创建了SpringApplication后,便回到了run方法中,继续分析SpringApplication.run方法。

创建startup

1
Startup startup = Startup.create();

Startup是SpringApplication的一个抽象静态内部类

记录了一些关于springboot应用启动的信息

1
2
3
4
private Duration timeTakenToStarted;
protected abstract long startTime();
protected abstract Long processUptime();
protected abstract String action();

startup的create方法会使用ClassUtil判断,该使用哪种startup类的实力

1
2
3
4
5
6
static Startup create() {
ClassLoader classLoader = Startup.class.getClassLoader();
return (ClassUtils.isPresent("jdk.crac.management.CRaCMXBean", classLoader)
&& ClassUtils.isPresent("org.crac.management.CRaCMXBean", classLoader))
? new CoordinatedRestoreAtCheckpointStartup() : new StandardStartup();
}

前者是一个基于 CRaC(Coordinated Restore At Checkpoint)技术的startup,可以快速启动,后面是一个标准的startup。

标准的startup类内部非常简单,也就不过多分析

ManagementFactory是 Java Management Extensions(JMX)的一部分,提供了一系列静态方法来获取 JVM 的管理接口,用于监控和管理 JVM 的运行状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static final class StandardStartup extends Startup {
private final Long startTime = System.currentTimeMillis();
@Override
protected long startTime() {
return this.startTime;
}
@Override
protected Long processUptime() {
try {
return ManagementFactory.getRuntimeMXBean().getUptime();
}
catch (Throwable ex) {
return null;
}
}
@Override
protected String action() {
return "Started";
}
}

配置shutdown hook

默认开启了shutdownhook,用于graceful shutdown

1
2
3
if (this.properties.isRegisterShutdownHook()) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}

配置BootstrapContext

1
DefaultBootstrapContext bootstrapContext = createBootstrapContext();

调用了createBootstrapContext方法

1
2
3
4
5
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}

DefaultBootstrapContext是ConfigurableBootstrapContext的默认实现类,ConfigurableBoostrapContext是一个接口,它继承了BootstrapRegistry, BootstrapContext接口。

BootstrapContext是在应用启动时,和应用的Environment后置处理期间,在ApplicationContext还没准备好时使用的上下文环境,提供了对某类单例的lazy access,即可能创建非常消耗资源,或者需要在ApplicationContext有效前共享的单例。BoosstrapContext适用于注册这些单例。

初始化和无头配置

初始化变量,作无头配置。

1
2
ConfigurableApplicationContext context = null;
configureHeadlessProperty();

配置listeners

1
2
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext,this.mainApplicationClass);

getRunListeners方法,也是通过getSpringFactoriesInstances方法,获取spring.factories中配置的listener

1
2
3
4
5
6
7
8
9
10
11
12
13
private SpringApplicationRunListeners getRunListeners(String[] args) {
ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
argumentResolver = argumentResolver.and(String[].class, args);
List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,
argumentResolver);
SpringApplicationHook hook = applicationHook.get();
SpringApplicationRunListener hookListener = (hook != null) ? hook.getRunListener(this) : null;
if (hookListener != null) {
listeners = new ArrayList<>(listeners);
listeners.add(hookListener);
}
return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
}

SpringApplicationRunListeners中搭载了一系列SpringApplicationRunListener接口的实现类,主要作用是监听SpringApplication的run方法,SpringApplicationRunListeners通过SpringFactoriesLoader加载,每次run都会创建一个SpringApplciationRunListener实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public interface SpringApplicationRunListener {

/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
* @param bootstrapContext the bootstrap context
*/
default void starting(ConfigurableBootstrapContext bootstrapContext) {
}

/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param bootstrapContext the bootstrap context
* @param environment the environment
*/
default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
}

/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
default void contextPrepared(ConfigurableApplicationContext context) {
}

/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
default void contextLoaded(ConfigurableApplicationContext context) {
}

/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @param timeTaken the time taken to start the application or {@code null} if unknown
* @since 2.6.0
*/
default void started(ConfigurableApplicationContext context, Duration timeTaken) {
}

/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @param timeTaken the time taken for the application to be ready or {@code null} if
* unknown
* @since 2.6.0
*/
default void ready(ConfigurableApplicationContext context, Duration timeTaken) {
}

/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}

}

创建ApplicationContext

一大团try-catch包围的创建ApplicationContext的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}

创建一个DefaultApplicationArguments实现了ApplicationArguments接口,这个接口就是记录SpringApplication启动时候使用的参数。

1
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

准备环境

1
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

prepareEnvironment方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
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;
}

getOrCreateEnvironment方法会直接获取环境,或者调用当前的applicationContextFactory中的默认创建环境方法。

1
2
3
4
5
6
7
8
9
10
11
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
WebApplicationType webApplicationType = this.properties.getWebApplicationType();
ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(webApplicationType);
if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
environment = ApplicationContextFactory.DEFAULT.createEnvironment(webApplicationType);
}
return (environment != null) ? environment : new ApplicationEnvironment();
}

比如,在ServletWebServerApplicationContextFactory类的createEnvironment方法中,返回了一个ApplciationServletEnvironment,该类又继承了StandardServletEnvironment类

1
2
3
4
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : new ApplicationServletEnvironment();
}

StandardServletEnvironment记录了一些变量

1
2
3
4
5
6
7
8
/** Servlet context init parameters property source name: {@value}. */
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";

/** Servlet config init parameters property source name: {@value}. */
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

/** JNDI property source name: {@value}. */
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

可以自定义propertySource,可以初始化propertySource.

WebApplicationContextUtils是一个工具类,提供了一些快捷的方法,可以取出给定的ServletContext的root WebApplicationContext

1
2
3
4
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}

接着看

1
configureEnvironment(environment, applicationArguments.getSourceArgs());

configureEnvironment是一个模版方法,用于按顺序进行配置,如果想完全控制环境定制,可以重写这个方法。

1
2
3
4
5
6
7
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
environment.setConversionService(new ApplicationConversionService());
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}

通知listeners,把applicationInfoPropertySource和DefaultPropertiesPropertySource移到environment的propertySource的最后面,就是字面上的最后面,list.addLast。随后把environment绑定到application上(就是把environment bind到 aplicationProperties上),如果不是自定义环境,看看是否要转换环境,随后把configurationPropertySources绑定到environment上。

1
2
3
4
5
6
7
8
9
10
11
12
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;

打印banner,就是springboot启动时候的那一坨

1
Banner printedBanner = printBanner(environment);
1
2
3
4
5
6
7
8
  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/

:: Spring Boot :: (v3.4.3)

context就是之前初始化为null的ConfigurableApplicationContext,调用createApplicationContext方法

1
2
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);

createApplicationContext方法会调用this.applicationContextFactory创建context

1
2
3
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.properties.getWebApplicationType());
}

this.applicationContextFactory是用ApplicationContextFactory.DEFAULT创建出来的一个DefaultApplicationContextFactory。

1
2
3
4
5
//SpringApplication内
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;

//ApplicationContextFactory接口内的
ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();

准备context,详细看prepareContext这个方法

1
2
3
4
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
startup.started();

首先,进行一些设置,包括给context配置环境,关闭之前的bootstrapContext,告诉listeners新的applicationContext已经准备好,同时输出一些log

1
2
3
4
5
6
7
8
9
10
11
context.setEnvironment(environment);
postProcessApplicationContext(context);
addAotGeneratedInitializerIfNecessary(this.initializers);
applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.properties.isLogStartupInfo()) {
logStartupInfo(context.getParent() == null);
logStartupInfo(context);
logStartupProfileInfo(context);
}
1
2
2025-03-07T13:26:34.321+08:00  INFO 3564 --- [SpringBoot] [           main] com.example.springboot.Application       : Starting Application using Java 21.0.6 with PID 3564 (D:\IdeaProjects\LearningJava\SpringBoot\target\classes started by Lenovo in D:\IdeaProjects\LearningJava)
2025-03-07T13:26:37.457+08:00 INFO 3564 --- [SpringBoot] [ main] com.example.springboot.Application : No active profile set, falling back to 1 default profile: "default"

接下来开始添加一些bean,首先是取出context里的beanFactory,先注册”springApplicationArguments”这个单例bean,

1
2
3
4
5
6
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}

判断beanFactory是否是AbstractAutowireCapableBeanFactory的实力,如果是,就允许循环依赖,如果还是DefaultListableBeanFactory的实力,就允许覆写bean的definition。

1
2
3
4
5
6
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences());
if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding());
}
}

判断是否懒加载,懒加载就添加一个bean factory后置处理器

1
2
3
if (this.properties.isLazyInitialization()) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}

是否keepalive,是的话添加一个listener

1
2
3
if (this.properties.isKeepAlive()) {
context.addApplicationListener(new KeepAlive());
}

keep alive监听器比较有意思,就是一个一直sleep的线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private static final class KeepAlive implements ApplicationListener<ApplicationContextEvent> {
private final AtomicReference<Thread> thread = new AtomicReference<>();
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
startKeepAliveThread();
}
else if (event instanceof ContextClosedEvent) {
stopKeepAliveThread();
}
}
private void startKeepAliveThread() {
Thread thread = new Thread(() -> {
while (true) {
try {
Thread.sleep(Long.MAX_VALUE);
}
catch (InterruptedException ex) {
break;
}
}
});
if (this.thread.compareAndSet(null, thread)) {
thread.setDaemon(false);
thread.setName("keep-alive");
thread.start();
}
}
private void stopKeepAliveThread() {
Thread thread = this.thread.getAndSet(null);
if (thread == null) {
return;
}
thread.interrupt();
}
}

回到前面

再添加一个后置处理器

1
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));

是否AOT optimization,再通知listeners,context已加载好。

1
2
3
4
5
6
7
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]));
}
listeners.contextLoaded(context);

刷新context

1
refreshContext(context);

刷新context后,可以看到控制台一些输出。

1
2
3
4
5
6
7
2025-03-07T13:45:25.483+08:00 ERROR 3564 --- [SpringBoot] [           main] o.a.catalina.core.AprLifecycleListener   : An incompatible version [1.2.26] of the Apache Tomcat Native library is installed, while Tomcat requires version [1.2.34]
2025-03-07T13:45:27.143+08:00 INFO 3564 --- [SpringBoot] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-03-07T13:45:27.258+08:00 INFO 3564 --- [SpringBoot] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2025-03-07T13:45:27.259+08:00 INFO 3564 --- [SpringBoot] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.36]
2025-03-07T13:45:27.541+08:00 INFO 3564 --- [SpringBoot] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2025-03-07T13:45:27.549+08:00 INFO 3564 --- [SpringBoot] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 31535 ms
2025-03-07T13:45:29.742+08:00 INFO 3564 --- [SpringBoot] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'

调用startup记录一下,判断是否添加一个startUpinfoLogger,通知一下listeners

1
2
3
4
5
startup.started();
if (this.properties.isLogStartupInfo()) {
new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup);
}
listeners.started(context, startup.timeTakenToStarted());

startupInfoLogger的输出

1
2025-03-07T13:47:56.034+08:00  INFO 3564 --- [SpringBoot] [           main] com.example.springboot.Application       : Started Application in 2040.684 seconds (process running for 2085.357)

呼叫runners

1
callRunners(context, applicationArguments);

进一步,对每个instance作call runner

1
2
3
4
5
6
7
8
9
10
11
private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] beanNames = beanFactory.getBeanNamesForType(Runner.class);
Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>();
for (String beanName : beanNames) {
instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName);
}
Comparator<Object> comparator = getOrderComparator(beanFactory)
.withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames));
instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args));
}

有两种runner,一种是普通的runner,另一种是commandLineRunner,commandLineRunner继承了Runner,可以通过ordered或者order注解排序,可以在bean里实现这个接口,spring application在启动时候就会调用run方法

1
2
3
4
5
6
7
8
9
private void callRunner(Runner runner, ApplicationArguments args) {
if (runner instanceof ApplicationRunner) {
callRunner(ApplicationRunner.class, runner, (applicationRunner) -> applicationRunner.run(args));
}
if (runner instanceof CommandLineRunner) {
callRunner(CommandLineRunner.class, runner,
(commandLineRunner) -> commandLineRunner.run(args.getSourceArgs()));
}
}

检查context是否处于启动状态,启动则通知listeners

最后返回context

1
2
3
4
5
6
7
8
9
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
return context;

至此,整个SpringApplication.run方法已分析完。