SpringFramework源码解析——Spring应用是如何启动的?
- 1. 背景
- 2. Spring应用是如何创建的?
- 3. Spring应用是如何运行的?
- 3.1. 记录启动时间
- 3.2. 创建引导上下文(createBootstrapContext)
- 3.3. 设置headless模式(configureHeadlessProperty)
- 3.4. 获取Spring应用运行监听器(getRunListeners)
- 3.5. 调用应用运行监听器的starting方法
- 3.6. 创建应用参数
- 3.7. 准备环境(prepareEnvironment)
- 3.8. 打印Banner(printBanner)
- 3.9. 创建应用上下文(createApplicationContext)
- 3.10. 准备应用上下文(prepareContext)
- 3.11. 刷新应用上下文(refreshContext)
- 3.12. 调用应用运行监听器的started方法
- 3.13. 回调Runner(callRunners)
- 3.14. 调用应用运行监听器的ready方法
- 3.15. 处理运行失败(handleRunFailure)
1. 背景
得益于SpringBoot的封装,我们只需要简单的几行代码便可启动一个Spring应用,如下示例:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
当调用SpringApplication#run(Class<?>, String...)
方法时,会创建并运行一个Spring应用。
2. Spring应用是如何创建的?
Spring应用的创建流程如下:
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();
// 设置引导上下文初始化器
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 设置应用上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置应用监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推测启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
对应源码所在位置:
SpringApplication#SpringApplication(ResourceLoader, Class<?>...)
。
2.1. 设置资源加载器
根据传入的构造器参数设置资源加载器ResouceLoader
。
// 设置资源加载器
this.resourceLoader = resourceLoader;
对应源码所在位置:
SpringApplication#SpringApplication(ResourceLoader, Class<?>...)
。
2.2. 设置初始资源类
根据传入的构造器参数设置初始资源类,供后续准备应用上下文过程中的加载Bean定义使用。
// 设置初始资源类
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
对应源码所在位置:
SpringApplication#SpringApplication(ResourceLoader, Class<?>...)
。
2.3. 推测Web应用类型(deduceFromClasspath)
根据classpath
下存在的类,来推测Web应用类型。WebApplicationType
有以下几种:
SERVLET
:基于servlet的Web应用,需要启动内嵌servlet类型的Web服务。REACTIVE
:基于reactive的Web应用,需要启动内嵌reactive类型的Web服务。NONE
:非Web应用,不需要启动内嵌Web服务。
static WebApplicationType deduceFromClasspath() {
// 有 webflux 所需类且没有 webmvc 和 jersey 所需类,对应 reactive
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// servlet 所需类非全都有,对应 none
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// servlet 所需类全都有,对应 servlet
return WebApplicationType.SERVLET;
}
对应源码所在位置:
WebApplicationType#deduceFromClasspath
。
2.4. 设置引导上下文初始化器
从META-INFO/spring.factories
中获取引导上下文初始化器BootstrapRegistryInitializer
列表,并设置到Spring应用中,供后续引导上下文初始化时使用。
// 设置引导上下文初始化器
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
对应源码所在位置:
SpringApplication#SpringApplication(ResourceLoader, Class<?>...)
。
2.5. 设置应用上下文初始化器(setInitializers)
从META-INFO/spring.factories
中获取应用上下文初始化器ApplicationContextInitializer
列表,并设置到Spring应用中,供后续应用上下文初始化时使用。
// 设置应用上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
对应源码所在位置:
SpringApplication#setInitializers
。
2.6. 设置应用监听器(setListeners)
从META-INFO/spring.factories
中获取应用监听器ApplicationListener
列表,并设置到Spring应用中,供后续接收发布的事件使用。
// 设置应用监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
对应源码所在位置:
SpringApplication#setListeners
。
2.7. 推测启动类(deduceMainApplicationClass)
获取堆栈信息,遍历栈帧StackFrame
,获取main
方法所在的类。
private Class<?> deduceMainApplicationClass() {
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
.walk(this::findMainClass)
.orElse(null);
}
private Optional<Class<?>> findMainClass(Stream<StackFrame> stack) {
return stack.filter((frame) -> Objects.equals(frame.getMethodName(), "main"))
.findFirst()
// 找到 main 方法所在的类
.map(StackWalker.StackFrame::getDeclaringClass);
}
对应源码所在位置:
SpringApplication#deduceMainApplicationClass
。
3. Spring应用是如何运行的?
Spring应用的运行流程如下:
public ConfigurableApplicationContext run(String... args) {
// 记录启动时间
Startup startup = Startup.create();
if (this.registerShutdownHook) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
// 创建引导上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 设置 headless 模式
configureHeadlessProperty();
// 获取 Spring 应用运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用应用运行监听器的 start 方法
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 设置应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 打印 Banner
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.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);
}
// 调用应用运行监听器的 started 方法
listeners.started(context, startup.timeTakenToStarted());
// 回调 Runner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
// 处理运行失败
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
if (context.isRunning()) {
// 调用应用运行监听器的 ready 方法
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
对应源码所在位置:
SpringApplication#run(String...)
。
3.1. 记录启动时间
记录Spring应用的启动时间。
Startup startup = Startup.create();
3.2. 创建引导上下文(createBootstrapContext)
- 实例化引导上下文
DefaultBootstrapContext
; - 使用引导上下文初始化器
BootstrapRegistryInitializer
初始化引导上下文。
private DefaultBootstrapContext createBootstrapContext() {
// 实例化默认引导上下文
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 初始化引导上下文
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}
对应源码所在位置:
SpringApplication#createBootstrapContext
。
3.3. 设置headless模式(configureHeadlessProperty)
Spring应用运行在服务端,java.awt.headless
指在没有显示设备的情况下能否正常运行,默认设置为true
。
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
对应源码所在位置:
SpringApplication#configureHeadlessProperty
。
3.4. 获取Spring应用运行监听器(getRunListeners)
- 从
META-INFO/spring.factories
中获取Spring应用运行监听器SpringApplicationRunListener
列表; - 使用组合模式将
SpringApplicationRunListener
列表封装为SpringApplicationRunListeners
。
private SpringApplicationRunListeners getRunListeners(String[] args) {
ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
argumentResolver = argumentResolver.and(String[].class, args);
// 从 spring.factories 中获取 Spring 应用运行监听器列表
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);
}
// 使用 SpringApplicationRunListeners 提供统一的调用入口(组合模式)
return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
}
对应源码所在位置:
SpringApplication#getRunListeners
。
3.5. 调用应用运行监听器的starting方法
调用应用运行监听器SpringApplicationRunListeners
的starting
方法。
// 调用应用运行监听器的 start 方法
listeners.starting(bootstrapContext, this.mainApplicationClass);
对应源码所在位置:
SpringApplicationRunListeners#starting
。
3.6. 创建应用参数
根据run
方法的入参创建应用参数。
// 创建应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
3.7. 准备环境(prepareEnvironment)
- 根据Web应用类型创建对应的Spring环境
Environment
; - 配置环境,将应用参数
ApplicationArguments
设置到Spring环境中; - 调用应用运行监听器的
environmentPrepared
方法; - 非自定义的Spring环境需要使用
EnvironmentConverter
进行环境转换。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 根据 Web 应用类型创建对应的环境(工厂模式)
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 调用应用运行监听器的 environmentPrepared 方法
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
// 非自定义的 Spring 环境需要进行环境转换
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
对应源码所在位置:
SpringApplication#prepareEnvironment
。SpringApplication#getOrCreateEnvironment
。SpringApplication#configureEnvironment
。SpringApplicationRunListeners#environmentPrepared
。
3.8. 打印Banner(printBanner)
根据Banner
的Mode
打印Banner,有以下几种类型:
OFF
:不打印Banner。LOG
:打印Banner到日志中。CONSOLE
:打印Banner到控制台上,该类型为默认Banner输出方式。
private Banner printBanner(ConfigurableEnvironment environment) {
// 不打印 Banner
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
// 打印 Banner 到日志中
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
// 打印 Banner 到控制台上
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
对应源码所在位置:
SpringApplication#printBanner
。
3.9. 创建应用上下文(createApplicationContext)
根据Web应用类型创建对应的应用上下文ConfigurableApplicationContext
,由DefaultApplicationContextFactory
进行分发:
WebApplicationType#SERVLET
对应ServletWebServerApplicationContextFactory
。WebApplicationType#REACTIVE
对应ReactiveWebServerApplicationContextFactory
。
protected ConfigurableApplicationContext createApplicationContext() {
// 根据 Web 应用类型创建应用上下文(工厂模式)
return this.applicationContextFactory.create(this.webApplicationType);
}
对应源码所在位置:
SpringApplication#createApplicationContext
。DefaultApplicationContextFactory#create
。DefaultApplicationContextFactory#createEnvironment
。DefaultApplicationContextFactory#getFromSpringFactories
。
3.10. 准备应用上下文(prepareContext)
- 将Spring环境
Environment
设置到应用上下文中; - 对应用上下文进行后置处理;
- 执行应用上下文初始化器
ApplicationContextInitializer
的初始化方法; - 调用应用运行监听器的
contextPrepared
方法; - 关闭引导上下文
DefaultBootstrapContext
; - 设置Bean工厂
BeanFactory
;- 注册单例的Bean
ApplicationArguments
和Banner
; - 设置是否允许循环依赖
allowCircularReferences
和是否允许Bean定义覆盖allowBeanDefinitionOverriding
; - 添加Bean工厂后置处理器
LazyInitializationBeanFactoryPostProcessor
和PropertySourceOrderingBeanFactoryPostProcessor
。
- 注册单例的Bean
- 从资源类中加载Bean定义;
- 调用应用运行监听器的
contextLoaded
方法。
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将 Spring 环境设置到应用上下文中
context.setEnvironment(environment);
// 对应用上下文进行后置处理
postProcessApplicationContext(context);
addAotGeneratedInitializerIfNecessary(this.initializers);
// 执行应用上下文初始化器的初始化方法
applyInitializers(context);
// 调用应用运行监听器的 contextPrepared 方法
listeners.contextPrepared(context);
// 关闭引导上下文
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// 设置 Bean 工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 注册单例的 Bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
// 设置是否允许循环依赖
autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
// 设置是否允许 Bean 定义覆盖
listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
// 添加 Bean 工厂后置处理器
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
if (this.keepAlive) {
context.addApplicationListener(new KeepAlive());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
if (!AotDetector.useGeneratedArtifacts()) {
// Load the sources
// 从初始资源类中加载 Bean 定义
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
}
// 调用应用运行监听器的 contextLoaded 方法
listeners.contextLoaded(context);
}
对应源码所在位置:
SpringApplication#prepareContext
。SpringApplication#postProcessApplicationContext
。SpringApplication#applyInitializers
。SpringApplication#load
。
3.11. 刷新应用上下文(refreshContext)
刷新应用上下文ConfigurableApplicationContext
。
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
对应源码所在位置:
SpringApplication#refreshContext
。
3.12. 调用应用运行监听器的started方法
调用应用运行监听器SpringApplicationRunListeners
的started
方法。
// 调用应用运行监听器的 started 方法
listeners.started(context, startup.timeTakenToStarted());
对应源码所在位置:
SpringApplicationRunListeners#started
。
3.13. 回调Runner(callRunners)
将应用上下文中的Runner
列表排序后进行回调,有以下两种Runner
:
ApplicationRunner
接受ApplicationArguments
作为入参。CommandLineRunner
接受args
作为入参。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
context.getBeanProvider(Runner.class).orderedStream().forEach((runner) -> {
if (runner instanceof ApplicationRunner applicationRunner) {
callRunner(applicationRunner, args);
}
if (runner instanceof CommandLineRunner commandLineRunner) {
callRunner(commandLineRunner, args);
}
});
}
对应源码所在位置:
SpringApplication#callRunners
。
3.14. 调用应用运行监听器的ready方法
调用应用运行监听器SpringApplicationRunListeners
的ready
方法。
// 调用应用运行监听器的 ready 方法
listeners.ready(context, startup.ready());
对应源码所在位置:
SpringApplicationRunListeners#ready
。
3.15. 处理运行失败(handleRunFailure)
- 处理退出码;
- 调用应用运行监听器
SpringApplicationRunListeners
的failed
方法; - 报告失败信息。
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
SpringApplicationRunListeners listeners) {
try {
try {
// 处理退出码
handleExitCode(context, exception);
if (listeners != null) {
// 调用应用运行监听器的 failed 方法
listeners.failed(context, exception);
}
}
finally {
// 报告失败信息
reportFailure(getExceptionReporters(context), exception);
if (context != null) {
context.close();
shutdownHook.deregisterFailedApplicationContext(context);
}
}
}
catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
对应源码所在位置:
SpringApplication#handleRunFailure
。
目录
- 1. 背景
- 2. Spring应用是如何创建的?
- 3. Spring应用是如何运行的?
- 3.1. 记录启动时间
- 3.2. 创建引导上下文(createBootstrapContext)
- 3.3. 设置headless模式(configureHeadlessProperty)
- 3.4. 获取Spring应用运行监听器(getRunListeners)
- 3.5. 调用应用运行监听器的starting方法
- 3.6. 创建应用参数
- 3.7. 准备环境(prepareEnvironment)
- 3.8. 打印Banner(printBanner)
- 3.9. 创建应用上下文(createApplicationContext)
- 3.10. 准备应用上下文(prepareContext)
- 3.11. 刷新应用上下文(refreshContext)
- 3.12. 调用应用运行监听器的started方法
- 3.13. 回调Runner(callRunners)
- 3.14. 调用应用运行监听器的ready方法
- 3.15. 处理运行失败(handleRunFailure)