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; private WebApplicationType webApplicationType; private List<ApplicationContextInitializer<?>> initializers; private List<ApplicationListener<?>> listeners; private Class<?> mainApplicationClass; public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
}
|
SpringApplication实例化时序图所示。
判定SpringBoot应用类型
SpringBoot共定义了三种类型,NONE
,SERVLET
,REACTIVE
。
- 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.DispatcherServlet
和org.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 应用程序启动过程探秘。
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源码理解实现分散配置