SpringBoot源码-启动
SpringBoot源码-启动
源码解析
SpringBoot 的启动很简单,一行代码就能完成:
@SpringBootApplication
public class MySpringApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(MySpringApplication.class, args);
}
}
但在这简单的代码背后,SpringBoot帮助我们完成了事件的注册,容器的生成。今天追踪源码,看一下SpringBoot的启动背后,究竟做了哪些事情。
SpringApplication.run()的执行会首先新建一个SpringApplication的实例,然后调用实例的run函数。
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
新建SpringApplication实例,会调用initialize方法。
public SpringApplication(Object... sources) {
initialize(sources);
}private void initialize(Object[] sources) {
// 将source加载近实例的sources属性中
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// bool类型,用来标识当前的环境是否为web环境
this.webEnvironment = deduceWebEnvironment();
// 将spring.factories中的ApplicationContextInitializer加载入实例的属性中
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 将spring.factories中的ApplicationListener加载入实例的属性中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// trace 递归找出main函数所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
其中:
-
deduceWebEnvironment
函数用来判断当前的Application是否为web环境,判断的方法是去尝试实例化Servlet
,ConfigurableWebApplicationContext
,如果成功,则认为是,反之则为否。 -
setInitializers
和setListeners
是通过扫描spring.factories文件来完成的。该文件的位置为jar包的resource/META-INF文件夹下,需要注意的是,文件可以存在于多个jar包中,这不影响springboot启动时的扫描,所以如果需要自定义启动类和监听类,只需要在自己的jar包中添加文件即可。
public ConfigurableApplicationContext run(String... args) {
// 构建了一个任务执行的观察器,通过start和stop可以完成任务的计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 获取SpringApplicationRunListeners,内部只有一个EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 通过EventPublishingRunListener向所有注册的listener类发送ApplicationStartedEvent事件
listeners.started();
try {
// 构造一个应用程序参数持有类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建spring容器
context = createAndRefreshContext(listeners, applicationArguments);
// spring容器创建完成后的回调任务
afterRefresh(context, applicationArguments);
// 通过EventPublishingRunListener向所有注册的listener类发送getFinishedEvent事件
listeners.finished(context, null);
// stop记录point
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 返回ConfigurableApplicationContext
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, ex);
throw new IllegalStateException(ex);
}
}
首先来说明一下
SpringApplicationRunListeners
,这个类在SpringBoot中只有一个实现:EventPublishingRunListener
。该类的ApplicationEventMulticaster
成员,其实充当了一个送报员的角色,在收到了报纸(主线程的通知)后,将根据订阅的人员名单(在Application中注册的Listener列表),进行事件的分发通知。执行的步骤如图:文章图片
@SpringApplicationRunListener执行步骤|center 具体的代码简略如下:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {private final ApplicationEventMulticaster multicaster;
……@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
publishEvent(getFinishedEvent(context, exception));
}……// 事件的分发
private void publishEvent(SpringApplicationEvent event) {
this.multicaster.multicastEvent(event);
}}
这样就很容易理解run函数中的
listener.start()
和listener.stop()
的作用了,用来标记启动的状态并通知各个注册的listener作出相应的操作。createAndRefreshContext
是创建Spring容器的过程:private ConfigurableApplicationContext createAndRefreshContext(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 定义spring容器 context
ConfigurableApplicationContext context;
// 根据之前的webEnvironment来初始化相应的环境
//(StandardEnvironment或者StandardServletEnvironment)
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境信息
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 通过EventPublishingRunListener向所有注册的listener类
// 发送ApplicationEnvironmentPreparedEvent事件
listeners.environmentPrepared(environment);
// 重新校验环境信息
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}// print 环境信息
if (this.bannerMode != Banner.Mode.OFF) {
printBanner(environment);
}// Create, load, refresh and run the ApplicationContext
// 创建Spring容器
context = createApplicationContext();
// 配置Spring容器
context.setEnvironment(environment);
// Spring容器创建之后需要做的回调方法
postProcessApplicationContext(context);
// 执行 SpringApplication 的初始化器
applyInitializers(context);
// 遍历调用SpringApplicationRunListener的contextPrepared方法
// 查看代码来看,只是将ApplicationEventMulticaster注册到Spring容器中
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}// Add boot specific singleton beans
// 把应用程序参数持有类注册到Spring容器中,并且是一个单例
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
// Load the sources
Set
Spring容器的创建:
protected ConfigurableApplicationContext createApplicationContext() {
Class> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 如果是web程序,那么构造AnnotationConfigEmbeddedWebApplicationContext容器
// 否则构造AnnotationConfigApplicationContext容器
// 这里也是利用了初始化SpringApplication的webEnvironment变量
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
Spring容器创建之后的后续操作:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.webEnvironment) {
// web环境 && Spring环境 注册实例命名生成器(后续章节详细讲)
if (context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext configurableContext = (ConfigurableWebApplicationContext) context;
if (this.beanNameGenerator != null) {
configurableContext.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
}
}// 设置资源loader
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader());
}
}
}
applyInitialzers,遍历初始化时加载的Initialzer类,并执行initialize方法。
比如ContextIdApplicationContextInitializer会设置应用程序的id;AutoConfigurationReportLoggingInitializer会给应用程序添加一个条件注解解析器报告等:
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);
}
}
Spring容器的refresh方法中会执行很多事情:例如BeanFactory的设置,BeanFactoryPostProcessor接口的执行、BeanPostProcessor接口的执行、自动化配置类的解析、条件注解的解析、国际化的初始化等等。后续详细说明。
run方法中在执行完毕
createAndRefreshContext
后,还执行了afterRefresh
函数protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
// 已弃用
afterRefresh(context, args.getSourceArgs());
// 加载runner
callRunners(context, args);
}private void callRunners(ApplicationContext context, ApplicationArguments args) {
List
自此而止,整个SpringBoot的启动就已经完成了,各种监听器,各种初始化器,各种Runner也已经完成了对于的工作
总结 SpringBoot在启动的过程中主要分为两个部分:
SpringApplication的初始化
和实例的run方法
:1. SpringApplication的初始化
- 设置source到实例的属性
- 检验web环境并标识
- 找出需要处理的所有
initializers
和listener
,设置到实例的属性中 - 找出main函数所在的类
- 1.设置事件分发器,充当送报员的角色
- 2.构造Spring容器
- 2.1 创建Spring容器
- 2.2 遍历执行
initializers
的initialize
方法 - 2.3 Spring容器的刷新(完成bean的解析、各种processor接口的执行、条件注解的解析等)
- 3.从Spring容器中找出
ApplicationRunner
和CommandLineRunner
接口的实现类并排序后依次执行
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- Hive常见问题汇总
- Android事件传递源码分析
- 注册分销商的骄傲
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- 如何启动改变
- ffmpeg源码分析01(结构体)