SpringBoot启动流程分析(二)- SpringApplication#run方法的执行逻辑
SpringApplication#run方法用于启动Spring应用,创建和刷新ApplicationContext
org.springframework.boot.SpringApplication#run(String...)
接收一个参数,即命令行参数
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 environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
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;
}
同样的,我们逐行分析
StopWatch stopWatch = new StopWatch(); stopWatch.start();
这两行代码创建了一个StopWatch,秒表,并且启动了这个秒表,来看一下这个类
public class StopWatch {
/**
* Identifier of this {@code StopWatch}.
* <p>Handy when we have output from multiple stop watches and need to
* distinguish between them in log or console output.
*/
private final String id;
private boolean keepTaskList = true;
/** 一系列任务 */
private final List<TaskInfo> taskList = new LinkedList<>();
/** Start time of the current task. */
private long startTimeNanos;
/** Name of the current task. */
@Nullable
private String currentTaskName;
@Nullable
private TaskInfo lastTaskInfo;
private int taskCount;
/** Total running time. */
private long totalTimeNanos;
// methods...
}
该类是一个非线程安全的类,用于记录一系列任务的执行时间和所有任务的总执行时间
Collection exceptionReporters = new ArrayList<>();
创建一个元素类型为SpringBootExceptionReporter的集合
/**
* 回调接口,用于记录SpringApplication在启动过程中的错误
* 它通过SpringFactoriesLoader来加载
* 并且它的实现类必须要提供一个只有一个参数,该参数的类型为ConfigurableApplicationContext的构造方法
*/
@FunctionalInterface
public interface SpringBootExceptionReporter {
/**
* 记录用户启动过程中的错误信息
* @param failure 错误信息
* @return 如果有错误被记录,返回true,否则返回false
*/
boolean reportException(Throwable failure);
}
该类是一个函数式接口,它只提供了一个方法
configureHeadlessProperty();
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true;
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
这个方法设置了一个名为java.awt.headless
的系统属性,首先取系统属性中的该属性的值,如果没有则给予其默认值true
这个属性是什么意思呢?
可以简单的理解为没有显示器、鼠标或键盘的环境下使用的模式,如果你的服务器没有显示设备,但是你又需要生成一些图像等,那这个模式下就允许你在没有显示设备的情况下使用canvas、panel等awt相关的方法,我们日常编写的服务器端就是这种模式
SpringApplicationRunListeners listeners = getRunListeners(args);
这行代码用于获取一系列的监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
- 调用getSpringFactoriesInstance方法来获取SpringApplicationRunListener接口的实现类
- 然后构造一个SpringApplicationRunListeners
SpringApplicationRunListener
这是一个SpringApplicaiton#run方法的监听器,它提供了一系列的生命周期方法,这些生命周期方法会随着run方法的执行而被调用
public interface SpringApplicationRunListener {
/**
* 当run方法第一次被执行时该方法被执行,可以用于做一些早期的初始化工作
*/
default void starting() {
}
/**
* 在environment准备好之后且ApplicationContext被创建之前,该方法被调用
*/
default void environmentPrepared(ConfigurableEnvironment environment) {
}
/**
* 在ApplicationContext倍创建并且做好了准备工作之后被调用,但此时sources还未被加载
*/
default void contextPrepared(ConfigurableApplicationContext context) {
}
/**
* 在ApplicationContext被加载后,并且还没有被刷新之前,该方法被调用
*/
default void contextLoaded(ConfigurableApplicationContext context) {
}
/**
* ApplicationContext被刷新并且应用已启动,但是CommandLineRunner和ApplicationRunner还没有被调用时该方法被调用
*/
default void started(ConfigurableApplicationContext context) {
}
/**
* 当run方法被执行完即当ApplicationContext、CommandLineRunner和ApplicationRunner都被调用后,该方法立即被调用
*/
default void running(ConfigurableApplicationContext context) {
}
/**
* 如果应用启动失败,该方法被调用
* @param context 传入ApplicationContext或者null(如果ApplicationContext还没有被创建的话)
* @param exception 异常信息
*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
再来看一下SpringApplicationRunListeners
class SpringApplicationRunListeners {
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
}
该类就是封装了一系列的SpringApplicationRunListener,在每次需要调用监听器的时候,就统一调用所有的监听器,并执行其方法,我们来分析其中的一个方法,其他的方法逻辑也是一样的
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
该方法最终会调用到SpringApplicationRunListerner的实现类EventPublishingRunListener中的starting方法
来看一下EventPublishingRunListener这个类以及其starting方法
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
/**
* 构造方法接收两个参数
* @param application SpringApplication实例
* @param args 命令行参数
*/
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
}
该类通过使用ApplicationEventMulticaster来发布一些SpringApplicationEvent, 很典型的 事件发布机制
ApplicationEventMulticaster
这是一个接口,用于管理一系列的ApplicationListener并将SpringApplicationEvent发布给这些Listener,这里用到了它的实现类SimpleAppliationEventMulticaster
private final SimpleApplicationEventMulticaster initialMulticaster;
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
- 首先调用一个参数的multicastEvent方法,传入ApplicationStartingEvent
- 再调用两个参数的multicastEvent,传入event,然后获取当前event的type并传入
- 接着就是获取对ApplicationStartingEvent事件感兴趣的Listener并调用他们的方法
看一下这里具体是怎么做的
先看一下resolveDefaultEventType方法,这是一个私有方法,里面会调用ResolvableType#forInstance方法来获取ResolvableType
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}
ResolvableType#forInstance
public static ResolvableType forInstance(Object instance) {
Assert.notNull(instance, "Instance must not be null");
if (instance instanceof ResolvableTypeProvider) {
ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();
if (type != null) {
return type;
}
}
return ResolvableType.forClass(instance.getClass());
}
该方法用于返回给定实例的resolvableType
- 首先判断给定实例是否是ResolvableTypeProvider类型
- 如果是,则将其转换为ResolvableTypeProvider,再调用#getResolvableType来获取其type
- 如果不是,则通过Resolvable#forClass来获取其type
然后就是两个参数的multicastEvent方法
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
- 首先判断传入的eventType是否为null,不为null则直接取该type,否则再次调用#resolvableDefaultEventType来获取event的type
- 接着调用#getApplicationListeners来获取对给定的eventType感兴趣的listener
- 如果线程池存在,则这里会通过异步的方式来执行listener的方法,否则就同步执行
listeners.starting();
这里就是开启listeners的starting方法
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
这里就是创建了一个ApplicationArguments的默认实现类DefaultApplicationArguments,用于访问命令行参数的
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
ConfigurableEnvironment,接口,继承了Environment接口,提供了用于设置active和default profile和操作properties的方法,该接口被大多数的Environment实现。允许客户端来设置和验证所需要的properties,也可以通过ConfigurablePropertyResolver对其进行定制化
prepareEnvironment
该方法用于创建、配置好一个Environment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
ConfigurableEnvironment environment = getOrCreateEnvironment();
根据webApplicationType来决定返回哪种类型的Environment,其类型有SERVLET,REACTIVE,NONE三种
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
- 首先判断this.environment是否存在,如果存在则直接返回
- 不存在则根据this.webApplicationType来返回对应的Environment实现类
configureEnvironment(environment, applicationArguments.getSourceArgs());
该方法是一个模版方法,子类可以重写这个方法来做一些定制化的操作,这里主要就是配置environment,配置profiles和propertySources
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
ConfigurationPropertySources.attach(environment);
该方法将ConfigurationPropertySource附加到environment上
listeners.environmentPrepared(environment);
调用监听器的environmentPrepared方法,通知监听器environment已经准备好了,可以进行响应的处理了
configureIgnoreBeanInfo(environment);
该方法用于设置忽略beanInfo的属性
Banner printedBanner = printBanner(environment);
该方法用于Banner打印相关操作,这一步操作之后就能在控制台看到Spring输出的信息了
context = createApplicationContext();
这里开始创建ApplicationContext了
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
- 根据webApplicationType来找到对应的Context的类
- 然后调用BeanUtils#instantiateClass方法来实例化Context
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] , context);
该方法跟之前分析的逻辑一样,从spring.factories文件中找到SpringBootExceptionReporter的实现类,并实例化
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
准备Context,为Context设置一些属性
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// 对context进行后置处理
postProcessApplicationContext(context);
// 在context刷新之前应用initializers
applyInitializers(context);
// 调用监听器的方法,这里开始,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]));
// 这里,context加载完成了,但是还没有被刷新
listeners.contextLoaded(context);
}
refreshContext(context);
刷新Context,将更新的值刷新到context中并应用
afterRefresh(context, applicationArguments);
当context被刷新后可以做一些操作,由子类自定义实现
stopWatch.stop();
计时结束,ApplicationContext配置完成,Environment配置完成
listeners.started(context);
调用监听器的started方法
callRunners(context, applicationArguments);
调用对应的Runner
listeners.running(context);
调用监听器的running方法