跟我学springboot第一讲-应用启动流程总体分析

跟我学springboot第一讲-应用启动流程总体分析

SpringBoot的出现,极大简化了基于Spring的Java应用的开发,赋予了Spring应用及其丰富的组件。但是越是简单的东西蕴含的风险越大,真正的用好Springboot,需要对它的原理有一定的了解,本文就带着大家认识下它的启动流程。

前言

new SpringApplicationBuilder(SearchBsBootstrap.class).run(args)一行代码,就可以帮你启动一个Springboot应用。

SpingApplication构造函数初始化

SpringBoot初始化入口类是SpringApplication,如下代码已经把核心方法列出,并画出了SpingApplication初始化的时序图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class SpringApplication {
//类加载器,一般情况下不需要指定
private ResourceLoader resourceLoader;
//主资源
private Set<Class<?>> primarySources;
//SpringBoot应用的类型
private WebApplicationType webApplicationType;
//加载的ApplicationContextInitializer实例
private List<ApplicationContextInitializer<?>> initializers;
//加载的ApplicationListener实例
private List<ApplicationListener<?>> listeners;
//执行类,即带有main方法的类
private Class<?> mainApplicationClass;

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//第一步,设置类加载器
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//第二步,设置主资源类,即@Configuration类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//第三步,推断SpringBoot应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//第四步,初始化所有ApplicationContextInitializer实例
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//第五步,初始化所有ApplicationListener实例
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//第六步,推断主执行类
this.mainApplicationClass = deduceMainApplicationClass();
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//第一步,获取ClassLoader
ClassLoader classLoader = getClassLoader();
//第二步,传入要加载的工厂类型和类加载器,返回要实例化的类
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//第三步实例化类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对实例进行排序,Order越小优先级越高
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

}

SpringApplication实例化时序图所示。

SpringApplication加载时序图

判定SpringBoot应用类型

SpringBoot共定义了三种类型,NONESERVLETREACTIVE

  • NONE: 不是web应用也不能运行在嵌入式web服务器中。不包含javax.servlet.Servlet或者不包含org.springframework.web.context.ConfigurableWebApplicationContext,就是NONE应用,注意两者缺一不可
  • SERVLET: 是一个基于servlet的web应用,也可以运行在嵌入式web服务器中。
  • REACTIVE: 是一个反应式web应用,也可以运行在嵌入式反应式web服务器中。包含org.springframework.web.reactive.DispatcherHandler,不包含org.springframework.web.servlet.DispatcherServletorg.glassfish.jersey.servlet.ServletContainer,则是REACTIVE应用。
1
2
3
4
5
6
7
8
9
10
11
12
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}

加载ApplicationContextInitializer和ApplicationListener

​ SpringFactoriesLoader.loadFactoryNames方法负责通过spring.factories,获取ApplicationContextInitializer和ApplicationListener的类的集合。如下是SringBoot下的spring.factories的示例。经过转化,配置文件变成了Properties,key和value都是一一对应配置文件的。举个例子key=org.springframework.boot.env.PropertySourceLoader,value={org.springframework.boot.env.PropertiesPropertySourceLoader,org.springframework.boot.env.YamlPropertySourceLoader}

Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader))最终返回ApplicationContextInitializer的子类。

类列表返回之后,调用createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)对子类进行实例化。最终被放入到SpringApplication类的成员变量中private List<ApplicationContextInitializer<?>> initializers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

SpingApplication#run()

图中是SpringApplication Run的总体示意图。具体的启动流程可以参见此图。此图创意来自SpringBoot 应用程序启动过程探秘

springBoot应用run示意图

Spring应用在启动过程中,存在七个阶段,对应SpringApplicationRunListener接口中的7个方法,每一个阶段完成以后,他们就负责发送通知,具体实现类EventPublishingRunListener

  • ApplicationStartingEvent:starting函数触发
  • ApplicationEnvironmentPreparedEvent:environmentPrepared方法触发,携带ConfigurableEnvironment对象
  • ApplicationContextInitializedEvent:contextPrepared方法触发,携带ConfigurableApplicationContext对象
  • ApplicationPreparedEvent:contextLoaded方法触发,携带ConfigurableApplicationContext对象
  • ApplicationStartedEvent:started 方法触发,携带ConfigurableApplicationContext对象
  • ApplicationReadyEvent:runing方法触发,携带ConfigurableApplicationContext对象
  • ApplicationFailedEvent:failed方法触发,携带ConfigurableApplicationContext对象

SpringApplicationRunListener的唯一实现类EventPublishingRunListener,内部使用了事件多播器SimpleApplicationEventMulticaster来管理多个事件监听器(Listener),使得每一个事件都可以被相关的Listener监听到,具体的Listener是SpringApplication的成员变量private List<ApplicationListener<?>> listeners,它是在SpringApplication构造函数初始化的时候设置的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void started(ConfigurableApplicationContext context);
void running(ConfigurableApplicationContext context);
void failed(ConfigurableApplicationContext context, Throwable exception);
}

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
}

参考文献

1、SpringBoot 应用程序启动过程探秘

2、Spring5源码解析-Spring框架中的事件和监听器

3、基于SpringBoot的Environment源码理解实现分散配置

评论