SpringApplication
一个可从 Java main
方法引导和启动 Spring
应用程序的类。
默认情况下,SpringApplication
将执行以下步骤来引导您的应用程序:
ApplicationContext
实例(取决于您的类路径)。CommandLinePropertySource
以将命令行参数注入到为Spring
属性中。CommandLineRunner
Bean加载并读取不同来源下的bean:
AnnotatedBeanDefinitionReader
加载的应用完全限定的类名所在包下的Bean。XmlBeanDefinitionReader
加载的声明在XML
文件中的Bean。GroovyBeanDefinitionReader
加载的groovy
脚本声明的Bean。ClassPathBeanDefinitionScanner
扫描 <context:component-scan>
节点所指定的软件包中的Bean。一般情况下,启动类所在的包应包含所有子模块的包,这样才能确保
SpringApplication
可以读取并加载到项目中声明的Bean。
在大多数情况下,可以直接从main
方法中调用静态run(Class, String[])
方法来引导应用程序:
package org.ifinal.finalframework.example;
@SpringBootApplication
public class FinalApplication {
public static void main(final String[] args) {
SpringApplication.run(FinalApplication.class);
}
}
像在文章你好,世界中的
SpringApplication.run(FinalApplication.class)
那样。
对于更高级的配置,可以在运行之前创建和定制SpringApplication
实例:
public static void main(String[]args){
SpringApplication application=new SpringApplication(MyApplication.class);
// ... customize application settings here
application.run(args)
}
通过IDE的代码提示功能,发现如下图的选项:
通过addListeners
或setListeners
方法,可以向应用中注册监听器(ApplicationListener
)。
application.addListeners(new ApplicationReadyEventListener());
既然是从SpringApplication.run(Class)
开始的,那就看看它都做了些什么吧:
SpringApplicatin.run(Class)
package org.springframework.boot;
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
}
上述代码的核心流程如下:
new SpringApplication(primarySources)
创建了一个SpringApplication
的实例;SpringApplication
的run()
方法。流程如图所示:
flowchart TD
subgraph one["SpringApplication"]
a["new SpringApplication(primarySources)"]-->b["run(args)"]
end
现在,来查看SpringApplication
的构造方法:
package org.springframework.boot;
public class SpringApplication {
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 保存配置源
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 初始化 WEB 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加载 spring.factories 文件中的 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载 spring.factories 文件中的 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断主应用类,这里是 FinalApplication,即参数类
this.mainApplicationClass = deduceMainApplicationClass();
}
}
第15行代码的字面意思是从classpath
推断WebApplicationType
,跟进deduceFromClasspath()
方法,发现Spring是通过判断特定的类是否存在来决定WebApplicationType
的。
第17、19两行代码中,分别加载了Spring的SPI扩展ApplicationContextInitializer
1和ApplicationListener
2
发现都调用了getSpringFactoriesInstances
方法,跟进发现一个关键的一行代码SpringFactoriesLoader.loadFactoryNames(type,classLoader)
, 该方法功能类似于JDK的SPI,用于加载Spring
SPI的类名:
package org.springframework.boot;
public class SpringApplication {
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[]{});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
}
SpringFactoriesLoader
是Spring SPI的加载器,用于从META-INF/spring.factories
配置文件中加载指定的SPI,详情请查看SpringFactory。
第21行deduceMainApplicationClass()
方法通过分析堆栈信息StackTraceElement
来推导主应用Class
,代码如下:
package org.springframework.boot;
public class SpringApplication {
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
}
至此,构造方法分析结束,收获收下几点:
classpath
下特定的Class
是否存在来决定WebApplicationType
的;SpringFactoriesLoader
加载了ApplicationContextInitializer
,ApplicationListener
扩展;Spring 通过分析堆栈信息StackTraceElement
来推导主应用Class
。
run(String[])
方法package org.springframework.boot;
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
// 开始停止监听
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
// 异常报告收集
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 开启事件监听
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 解析启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备 ConfigurableEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印 Banner
Banner printedBanner = printBanner(environment);
// 创建 ApplicationContext
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[]{ConfigurableApplicationContext.class}, context);
// 准备 ApplicationContext
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新 ApplicationContext
refreshContext(context);
// 后置刷新回调,用于子类扩展
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
}
flowchart TD
subgraph one["run"]
a1["SpringApplicationRunListeners listeners = getRunListeners(args)"]-->a2["listeners.starting()"]
a2-->a3["ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments)"]
a3-->a4["Banner printedBanner = printBanner(environment)"]
a4-->a5["context = createApplicationContext()"]
a5-->a6["prepareContext(context, environment, listeners, applicationArguments, printedBanner)"]
a6-->a7["refreshContext(context)"]
a7-->a8["afterRefresh(context, applicationArguments)"]
a8-->a9["listeners.started(context)"]
a9-->a10["listeners.running(context)"]
end
a6-->two
subgraph two["prepareContext"]
b1["listeners.contextPrepared(context)"]-->b2["listeners.contextLoaded(context)"]
end
第16行getFunListeners(args)
通过SpringFactoriesLoader
加载了扩展SpringApplicationRunListener
并实例了SpringApplicationRunListeners
对象。
SpringApplicationRunListeners
是SpringApplicationRunListener
的集合表现形式,用于触发所有SpringApplicationRunListener
实例。
第17行调用了SpringApplicationRunListeners
的starting()
方法。
第20行创建了一个ApplicationArguments
的实例。
第22行创建了一个ConfigurableEnvironment
的实例,并在方法内调用了SpringApplicationRunListeners
的environmentPrepared(environment)
方法。
第27行创建了一个ConfigurableApplicationContext
的实例。
第28行通过SpringFactoriesLoader
加载了扩展SpringBootExceptionReporter
。
第31行prepareContext
方法用于准备ConfigurableApplicationContext
,稍后单独分析,现在直接跳过进行下一行。
第33行 refreshContext(context)
最终调用了ConfigurableApplicationContext
的refreah()
方法。
第34行afterRefresh(context, applicationArguments)
是一个空的方法,用于子类扩展。
第40行调用了SpringApplicationRunListeners
的started(context)
方法。
第41行通过callRunners()
调用了所有ApplicationRunner
和CommandLineRunner
实例的run
方法。
第43行当有异常时,调用了SpringApplicationRunListeners
的failed(context, exception)
方法。
第48行调用了SpringApplicationRunListeners
的running(context)
方法。
现在,回过头来分析prepareContext
方法
package org.springframework.boot;
public class SpringApplication {
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 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);
}
}
第9行调用了ApplicationContextInitializer
的initialize(context)
。
第10行调用了SpringApplicationRunListeners
的contextPrepared(context)
方法。
第31行内部创建了一个BeanDefinitionLoader
实例并调用了load()
方法。
BeanDefinitionLoader
用于加载BeanDefinition
,包含以下读取器:
- 由
AnnotatedBeanDefinitionReader
加载的应用完全限定的类名所在包下的Bean。- 由
XmlBeanDefinitionReader
加载的声明在XML
文件中的Bean。- 由
GroovyBeanDefinitionReader
加载的groovy
脚本声明的Bean。- 由
ClassPathBeanDefinitionScanner
扫描<context:component-scan>
节点所指定的软件包中的Bean。
第32行调用了SpringApplicationRunListeners
的contextLoaded(context)
方法。
至此,SpringApplication 的核心启动流程就完成了。
本节以SpringApplication.run(Class)
方法为切入点,通过分析构造方法和run
方法等源码,获取了以下知识点:
SpringFactoriesLoader
加载了以下扩展点(SPI):
refreshContext(context)
刷新环节中,指向了ConfigurableApplicationContext
接口的refresh
方法。本文结束。