SpringBoot启动分析
SpringBoot版本:2.0.6.RELEASE
本文粗浅地分析SpringBoot启动流程,了解SpringBoot和Spring容器的一些扩展点。
入口
从我们指定的启动类中的main方法开始,run方法传入了主类的Class对象。
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Main.class, args);
}
追踪run方法可以看到,首先new了一个SpringApplication对象,接着调用了它的run方法。
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args); // 将主类的Class对象放在只有一个元素的数组里传给下面的run方法
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
SpringApplication实例化阶段
SpringApplication的描述是这样的:
本类从Java main方法引导和启动一个Spring应用。默认情况下引导步骤如下:
- 创建
ApplicationContext实例;- 注册
CommandLinePropertySource将命令行参数暴露为Spring属性;- 刷新应用上下文,加载所有单例bean;
- 触发所有
CommandLineRunnerbean。
实例化SpringApplication的构造方法。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader; // null
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 数组转为有序集合LinkedHashSet
// 推断webApplication类型:NONE/SERVLET/REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化spring.factories中的ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 初始化spring.factories中的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断引导类
this.mainApplicationClass = deduceMainApplicationClass();
}
getSpringFactoriesInstances方法在META-INF/spring.factories文件中分别查找ApplicationContextInitializer以及ApplicationListener,获取类的全路径名,然后通过反射逐个加载类并实例化。

ApplicationContextInitializer和ApplicationListener的用途:
在SpringBoot应用中,classpath上会包含许多jar包,有些jar包需要在
ConfigurableApplicationContext#refresh()调用之前对应用上下文做一些初始化动作,因此它们会提供自己的ApplicationContextInitializer实现类。ApplicatonContext事件机制是观察者模式。每当ApplicatonContext发布ApplicationEvent事件时,ApplicationListener将会被触发。例如,
ConfigFileApplicationListener就是利用这个机制来在环境准备好之后加载配置文件的。
SpringApplication运行阶段
SpringApplication#run()是SpringBoot启动运行的核心方法。
public ConfigurableApplicationContext run(String... args) {
// 秒表,用于对代码进行计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 初始化所有SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 创建ApplicationArguments对象,处理args
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 设置系统属性spring.beaninfo.ignore
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
// 实例化应用上下文对象
context = createApplicationContext();
// 初始化所有SpringBootExceptionReporter,用来报告启动过程中的错误
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备应用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新上下文
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;
}
SpringApplicationRunListeners
run方法中各个阶段伴随着listeners的调用,这里首先介绍listeners——SpringApplicatonRunListeners。SpringApplicatonRunListeners是SpringApplicatonRunListener的集合。
SpringApplicationRunListener也是在spring.factories中定义的。它的作用是,在SpringBoot启动的过程中可以通过SpringApplicationRunListener接口回调来让用户在启动的各个流程中可以加入自己的逻辑。
接口定义如下:
public interface SpringApplicationRunListener {
// run方法启动后调用
void starting();
// Environment构建完成时调用
void environmentPrepared(ConfigurableEnvironment environment);
// ApplicationContext构建完成时调用
void contextPrepared(ConfigurableApplicationContext context);
// ApplicationContext完成加载,但没有刷新之前调用
void contextLoaded(ConfigurableApplicationContext context);
// ApplicationContext完成刷新并启动后,callRunners执行之前调用
void started(ConfigurableApplicationContext context);
// run()方法执行的最后一步调用
void running(ConfigurableApplicationContext context);
// 运行出错时调用
void failed(ConfigurableApplicationContext context, Throwable exception);
}
SpringApplicationRunListener与前面的ApplicationListener区别是,SpringApplicationRunListener的生命周期是在run方法中,每次运行run都会重新创建实例;ApplicationListener在SpringApplication构造好之后不会再改变。
另外ApplicationListener监听的事件其实是靠EventPublishingRunListener触发的。推送的SpringApplicationEvent及对应的RunListener方法如下:
ApplicationStartingEvent- startingApplicationEnvironmentPreparedEvent- eviromentPreparedApplicationPreparedEvent- contextLoadedApplicationStartedEvent- startedApplicationReadyEvent- runningApplicationFailedEvent- failed
contextLoaded方法没有事件推送。
ApplicationArguments
ApplicationArguments接口用来处理main方法的参数args。SpringBoot提供了默认实现DefaultApplicationArguments。
ApplicationArguments的主要方法就两个。
/**
* 获取option参数值列表,即以--开头的参数
* 例如:
* 1. --foo 返回空列表
* 2. --foo=bar 返回["bar"]
* 3. --foo=bar --foo=baz 返回["bar", "baz"]
* option不存在时返回null。
*/
List<String> getOptionValues(String name);
/**
* 获取non-option参数列表
*/
List<String> getNonOptionArgs();
准备环境
这个阶段创建并配置了Environment。
Environment即应用程序的运行环境,由两部分组成:profiles和properties。profiles就是spring.profiles.active的值;properties指键值对。
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根据webApplicationType创建对应的ConfigurableEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置configureEnvironment
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
// 将configureEnvironment绑定到SpringApplication对象
bindToSpringApplication(environment);
// 若ConfigurableEnvironment非自定义的,则转换其类型
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
ConfigurableEnvironment的继承关系如下。

prepareEnvironment方法返回了ConfigurableEnvironment接口的实例,根据webApplicationType返回不同的子类实例:
- 非web应用
NONE-StandardEnvironment - Servlet应用
SERVLET-StandardServletEnvironment - Reactive应用
REACTIVE-StandardReactiveWebEnvironment
配置configureEnvironment分为方面,一是configurePropertySources,处理应用中所有PropertySource;二是configureProfiles,为应用配置激活的profiles。
创建和准备上下文
根据web应用类型创建应用上下文ConfigurableApplicationContext实例,然后准备上下文。
准备上下文主要的一步是执行一系列的初始化器ApplicationContextInitializer。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 向上下文设置前面准备好的environment
context.setEnvironment(environment);
// 上下文后置处理
postProcessApplicationContext(context);
// 执行之前收集的ApplicationContextInitializer
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// 注册单例bean:applicationArguments
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
// 注册单例bean:printedBanner
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 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);
}
刷新上下文
跟踪refreshContext()可以发现最终调用了AbstractApplicationContext#refresh()方法。
刷新上下文过程中初始化了所有bean。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) { // 加锁
// Prepare this context for refreshing.
// 为应用上下文刷新做准备:设置时间、记录刷新日志、初始化属性源中的占位符、验证必要的属性
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 获取bean factory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 初始化bean factory
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 注册所有BeanFactoryPostProcessor
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 调用BeanFactoryPostProcessor各个实现类的方法
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化MessageSource
initMessageSource();
// Initialize event multicaster for this context.
// 初始化ApplicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 子容器特殊的刷新逻辑,例如创建WebServer
onRefresh();
// Check for listener beans and register them.
// 注册实现了ApplicationListener接口的bean
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化所有非懒加载单例
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成上下文刷新,处理LifeCycle
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
BeanFactoryPostProcessor和BeanPostProcessor
BeanFactoryPostProcessor和BeanPostProcessor都是Spring初始化bean时对外暴露的扩展点。
BeanFactoryPostProcessor的定义如下。
/*
* beanFactory的后置处理器
*/
@FunctionalInterface
public interface BeanFactoryPostProcessor {
// 在bean定义加载之后、实例化之前执行,可以在这里获取和修改bean定义(BeanDefinition)
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
BeanPostProcessor的定义如下。
/**
* bean的后置处理器
*/
public interface BeanPostProcessor {
// bean初始化操作(例如afterPropertiesSet或自定义的init方法)之前执行
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// bean初始化之后的后置处理器
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
ApplicationListener
前面提到,在SpringApplication实例化阶段,注册了spring.factories中所有的ApplicationListner。@Configuration注解的ApplicationListener当时并没有被注册,它们是在刷新上下文阶段注册的。
因此实现了@Configuration注解的ApplicationListener无法监听到注册之前推送的事件,例如:ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent。
LifeCycle
执行finishRefresh时,会由LifecycleProcessor注册所有Lifecycle并按序执行其start方法。
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
有时候我们需要在Spring初始化所有bean之后,接着执行一些启动需要的异步服务,就可以使用Lifecycle。
一般使用其子接口SmartLifecycle,phase越小优先级越高。

总结
SpringBoot启动主要步骤概括如下:
- 初始化SpringApplication实例
- 创建并准备环境
- 创建应用上下文
- 准备应用上下文
- 刷新上下文