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包中添加文件即可。
SpringApplication的实例化完成后,就是执行run方法:
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列表),进行事件的分发通知。执行的步骤如图:
具体的代码简略如下:
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<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
// 通过EventPublishingRunListener广播发送ApplicationPreparedEvent给所有注册的listener类
listeners.contextLoaded(context);
// Refresh the context
// 刷新Spring容器
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
return context;
}
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<Object> runners = new ArrayList<Object>();
// 找出Spring容器中ApplicationRunner接口的实现类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 找出Spring容器中CommandLineRunner接口的实现类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序
AnnotationAwareOrderComparator.sort(runners);
// 遍历执行run
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
自此而止,整个SpringBoot的启动就已经完成了,各种监听器,各种初始化器,各种Runner也已经完成了对于的工作
总结
SpringBoot在启动的过程中主要分为两个部分:SpringApplication的初始化
和实例的run方法
:
1. SpringApplication的初始化
- 设置source到实例的属性
- 检验web环境并标识
- 找出需要处理的所有
initializers
和listener
,设置到实例的属性中 - 找出main函数所在的类
2. 实例的run方法
- 1.设置事件分发器,充当送报员的角色
- 2.构造Spring容器
- 2.1 创建Spring容器
- 2.2 遍历执行
initializers
的initialize
方法 - 2.3 Spring容器的刷新(完成bean的解析、各种processor接口的执行、条件注解的解析等)
- 3.从Spring容器中找出
ApplicationRunner
和CommandLineRunner
接口的实现类并排序后依次执行