0
点赞
收藏
分享

微信扫一扫

全网最详细的介绍SpringBoot启动过程源码分析

概述

上一篇我们介绍了SpringBoot的自动装配的知识,这一篇我们将介绍SpringBoot最核心的知识点,SpringBoot应用的启动过程。这个启动过程比较复杂,在此我只介绍核心的知识点。其启动过程大概分为两步。1. 初始化SpringApplication对象,2.执行SpringApplication对象的run方法。

SpringBoot启动流程图(以SpringBoot 1.5.8.RELEASE为例)

全网最详细的介绍SpringBoot启动过程源码分析_springboot
那我们就根据上面的启动流程图进行分析。

初始化SpingApplication对象

我们直接找到初始化​​SpingApplication​​​对象的​​initialize​​方法。

private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//检查当前环境是否是web环境
this.webEnvironment = deduceWebEnvironment();
//初始化ApplicationContextInitializer的实现类
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//初始化ApplicationListener的实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

如上初始化​​SpringApplication​​对象,主要的步骤有两步

  1. 加载spring.factories中ApplicationContextInitializer的配置类
  2. 加载spring.factories中ApplicationListener的配置类
    都是通过​​​SpringFactoriesLoader​​​找到​​META-INF/spring.factories​​​文件下配置了​​ApplicationContextInitializer​​​和​​ApplicationListener​​​两个接口的实现类,并且进行实例化。
    全网最详细的介绍SpringBoot启动过程源码分析_springboot_02
    其中​​​ApplicationContextInitializer​​​接口主要目的是​​ConfigurableApplicationContext​​​做refresh之前,对​​ConfigurableApplicationContext​​实例做进一步的设置或处理。如下图所示:
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}

而​​ApplicationListener​​则是一个监听器,他是Spring框架对Java事件监听机制的⼀种框架实现。

执行Run方法

说完了初始化​​SpingApplication​​​对象的过程,接下来让我们看看​​run()​​方法的执行逻辑。

public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//1
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//2
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//3
Banner printedBanner = printBanner(environment);
//4
context = createApplicationContext();
//5
analyzers = new FailureAnalyzers(context);
//6
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//7
refreshContext(context);
//8
afterRefresh(context, applicationArguments);
//9
listeners.finished(context, null);
stopWatch.stop();
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}

如上,就是执行run方法的主要逻辑,主要分为9个步骤。

1. 加载SpringApplicationRunListeners

首先第一步是:通过SpringFactoriesLoader 到​​META-INF/spring.factories​​​查找并加载所有的​​SpringApplicationRunListeners​​​,通过​​start()​​​方法通知所有的​​SpringApplicationRunListener​​​,本质上这是一个事件发布者,他在SpringBoot应用启动的不同阶段会发布不同的事件类型。​​SpringApplicationRunListener​​​接口只有一个实现类​​EventPublishingRunListener​​​,也就是说​​SpringApplicationRunListeners​​​类的​​List<SpringApplicationRunListener> listeners​​​中只会生成一个​​EventPublishingRunListener​​​实例。那么​​SpringApplicationRunListeners​​​是如何发布事件类型的呢?首先我们看下​​SpringApplicationRunListener​​这个接口。

public interface SpringApplicationRunListener {
/**
* run方法刚执行时通知
*/
void starting();
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
Environment准备好,ApplicationContext被创建好之前通知
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
ApplicationContext被创建好之后,但是资源加载好之前通知
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
ApplicationContext被加载好之后,但是没有被刷新之前通知
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes.
* @param context the application context or null if a failure occurred before the
* context was created
应用启动完成之后通知
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
}

如上我们看到​​SpringApplicationRunListener​​​监听器SpringBoot应用启动的不同阶段都会有相应的监听通知。通知贯穿了SpringBoot应用启动的完成过程。我们以​​environmentPrepared​​​通知为例看看,​​SpringApplicationRunListener​​​是如何发布事件类型的,在其实现类​​EventPublishingRunListener​​​中有属性名为​​initialMulticaster​​​的​​SimpleApplicationEventMulticaster​​​实例。在​​environmentPrepared​​​方法中调用了​​SimpleApplicationEventMulticaster​​​的​​multicastEvent​​​方法,说明发布过程被委托给了​​SimpleApplicationEventMulticaster​​​类,其中在​​multicastEvent​​方法中指定了相应的事件类型。

public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}

2. 创建并配置当前应用将要使用的环境

private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 获取创建的环境,如果没有则创建,如果是web环境则创建StandardServletEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置Environment:配置profile以及properties
configureEnvironment(environment, applicationArguments.getSourceArgs());
//调⽤SpringApplicationRunListener的 environmentPrepared()⽅法,通知事件监听者:应⽤的Environment已经准备好
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}

第二步是创建并配置当前应用的环境(Environment),Environment用于描述应用程序当前的运行环境,其抽象了两方面的内容:1. 配置文件(profile)和属性(properties),我们知道不同的环境(开发环境,测试环境,发布环境)可以使用不同的属性配置,这些属性配置可以从配置文件,环境变量,命令行参数等来源获取。因此,当Environment准备好之后,在整个应用的任何时候,都可以获取这些属性。
所以,第二步的做的事情主要有如下三件:

  1. 获取创建的环境(Environment),如果没有则创建,如果是web环境则创建​​StandardServletEnvironment​​​,如果不是的话则创建​​StandardEnvironment​​。
  2. 配置环境(Environment):主要是配置profile和属性properties。
  3. 调用​​SpringApplicationRunListener​​​的​​environmentPrepared​​方法,通知事件监听者:应用环境(Environment)已经准备好了。

3.设置SpringBoot应用在启动时输出的Banner。

第三步是设置SpringBoot应用在启动时输出的Banner,默认的Banner如下图所示:
全网最详细的介绍SpringBoot启动过程源码分析_spring_03
当然我们也可以修改默认的Banner,修改的方法就是在resources下新建一个banner.txt文件,替换掉默认的banner

4. 根据是否是web项目,来创建不同的ApplicationContext容器

protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

第四步是:创建不同的ApplicationContext容器;经过了前面的初始化SpingApplication对象的过程,我们就已经知道了当前应用的环境,那么如果是web应用,则创建​​AnnotationConfigEmbeddedWebApplicationContext​​​对象,否则创建​​AnnotationConfigApplicationContext​​对象。

5. 创建一系列的FailureAnalyzer

FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader);
this.analyzers = loadFailureAnalyzers(this.classLoader);
prepareFailureAnalyzers(this.analyzers, context);
}

第五步是创建​​FailureAnalyzer​​​的代码如上所示:创建的流程依然是通过​​SpringFactoriesLoader​​​获取所有的​​FailureAnalyzer​​​接口的实现类名称,然后创建对应的实例。​​FailureAnalyzer​​的作用是用于分析故障并提供相关的诊断信息。

6. 初始化ApplicationContext

前面第四步,我们已经创建好了与本应用环境相匹配的ApplicationContext实例,那么第六步,就是对​​ApplicationContext​​进行初始化了。这一步也是比较核心的一步。首先让我们来看看实现逻辑的相关代码:

private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//1. 将准备好的Environment设置给ApplicationContext
context.setEnvironment(environment);
postProcessApplicationContext(context);
//2. 遍历调用所有的ApplicationContextInitializer的 initialize() 方法来对已经创建好的 ApplicationContext 进行进一步的处理。
applyInitializers(context);
//3. 调用SpringApplicationRunListeners的 contextPrepared() 方法,通知所有的监听者,ApplicationContext已经准备完毕
listeners.contextPrepared(context);
//4. 将applicationArguments实例注入到IOC容器
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
//5. 将printedBanner实例注入到IOC容器
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
//6. 加载资源,这里的资源一般是启动类xxxApplication
Set<Object> sources = getSources();
//7. 将所有的bean加载到容器中
load(context, sources.toArray(new Object[sources.size()]));
//8. 调⽤SpringApplicationRunListener的 contextLoaded()⽅法,通知所有的监听者:ApplicationContext已经装载完毕
listeners.contextLoaded(context);
}

如上就是初始化​​ApplicationContext​​的主要逻辑,主要有如下逻辑:

  1. 将准备好的​​Environment​​​设置给​​ApplicationContext​
  2. 遍历调用所有的​​ApplicationContextInitializer​​​的​​initialize()​​​ 方法来对已经创建好的​​ApplicationContext​​ 进行进一步的处理。
  3. 调用​​SpringApplicationRunListeners​​​的​​contextPrepared()​​​ 方法,通知所有的监听者,​​ApplicationContext​​已经准备完毕
  4. 将​​applicationArguments​​实例注入到IOC容器。
  5. 将​​printedBanner​​实例注入到IOC容器,这个就是第三步生成的Banner的实例。
  6. 加载资源,这里的资源一般是启动类xxxApplication
  7. 将所有的bean加载到容器中
  8. 调⽤​​SpringApplicationRunListeners​​​的​​contextLoaded()​​​⽅法,通知所有的监听者:​​ApplicationContext​​已经装载完毕。

7. 调用ApplicationContext的refresh() 方法

第七步就是调用ApplicationContext的refresh() 方法,完成IOC容器的最后一道工序,为何要刷新容器呢?主要就是插手容器的启动。这里的 ​​SpringApplication​​​的 ​​refresh​​​方法最终还是调用到​​AbstractApplicationContext​​​的​​refresh​​​方法。
说到​​​AbstractApplicationContext​​​的​​refresh​​​方法,就要回到我们前面说的Bean的生命周期。一个是​​BeanFactoryProcessor​​​接口,用于插手容器的初始化。另外一个是​​BeanPostProcessor​​接口,用于插手Bean的实例化。

8.查找当前context中是否注册

查找当前context中是否注册有CommandLineRunner和ApplicationRunner,
如果有则遍历执行它们。

9.执行所有SpringApplicationRunListener的finished() 方法

对run方法的断点调试

  1. 1.5.8 版本的
    全网最详细的介绍SpringBoot启动过程源码分析_spring_04
  2. 2.1.3 版本的
    全网最详细的介绍SpringBoot启动过程源码分析_初始化_05

总结

这就是Spring Boot的整个启动流程,其核⼼就是在Spring容器初始化并启动的基础上加⼊各种扩展点,这些扩展点包括:
ApplicationContextInitializer、ApplicationListener以及各种BeanFactoryPostProcessor等等。你对整个流程的细节不必太过关注,你只要理解这些扩展点是在何时如何⼯作的,能让它们为你所⽤即可。


举报

相关推荐

0 条评论