代码版本 v2.1.7.RELEASE
大致分析一下SpringBoot启动过程中做了什么
一般启动一个SpringBoot项目
public static void main(String[] args) {
SpringApplication.run(Test.class, args);
}
run的代码如下
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
启动代码分析,做了两件事
- 创建SpringApplication对象
- 执行run方法
下面依次分析两个步骤
创建SpringApplication对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//初始化主类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断当前应用类型类型,这个会影响到创建Environment,ConfigApplicationContext类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化 ApplicationContextInitializer类,之后会在run过程中用到
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取所有监听器,之后会在run过程中用到
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获取main函数所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath()
这个就是通过classpath下是否存在指定的类来判断当前SpringBoot应该属于那种类型,现阶段包含三种类型,之后创建spirng容器(ConfigurableApplicationContext),还有Environment(ConfigurableEnvironment)时候回根据这个类型来判断,创建相应的
- NONE 非WEB容器,这个类型不会启动web server(内置的tomcat...)
- SERVLET WEB容器,启动内嵌的web server
- REACTIVE WEB响应式容器,源码解释是 *The application should run as a reactive web application and should start an embedded reactive web server. *
getSpringFactoriesInstances(...)使用
关于getSpringFactoriesInstances(...) 作用就是从META-INF/spring.factories获取指定的实现类,SpringBoot下spring.factories内容如下,其中 setInitializers(...),setListeners(...)都是通过此方法加载org.springframework.context.ApplicationContextInitializer、org.springframework.context.ApplicationListener下的所有类,然后赋值给 initializers、listeners变量,这两个变量具体作用会在run中体现出来
# 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
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
执行run方法
看一下SpringBoot run方法的整体流程,代码如下
public ConfigurableApplicationContext run(String... args) {
//计时工具,记录启动总共耗时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//获取SpringApplicationRunListener,从META-INF/spring.factories获取 SpringApplicationRunListener,此处为EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//广播 ApplicationStartingEvent事件
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建environment,并做准备
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//创建Spring上下文,会根据webApplicationType 类型的不同创建不同的上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//<2>.准备Spring上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新Spring上下文
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//广播ApplicationStartedEvent事件
listeners.started(context);
//从spring中获取所有 ApplicationRunner,CommandLineRunner 执行
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//广播ApplicationReadyEvent事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
prepareEnvironment
这个方法是对环境变量的操作
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment 此处也会根据 webApplicationType 判断需要创建什么类型的environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置environment,包含PropertySources ,Profiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
//广播ApplicationEnvironmentPreparedEvent事件
listeners.environmentPrepared(environment);
//将配置文件中spring.main开头的配置绑定在SpringApplication中,可以通过配置文件控制SpringApplication的参数值
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
getOrCreateEnvironment
Environment 里面存放的东西就是从 System中、application.properties/yml文件中的数据
这个方法会根据前文说的 WebApplicationType来具体生成不同的ConfigurableEnvironment,ConfigurableEnvironment作用这个类主要作用就是存放解析全局性的参数,包括,根据WebApplicationType类型生成的类型有以下几种
- SERVLET 对应 StandardServletEnvironment
- REACTIVE 对应 StandardReactiveWebEnvironment
- 默认的 StandardEnvironment
configureEnvironment
设置ConfigurableEnvironment的conversionService 设置ConfigurableEnvironment的MutablePropertySources 设置ConfigurableEnvironment的activeProfiles,从spring.profiles.active获取的 + SpringApplication类中的additionalProfiles的值,例如 我设置 spring.main.allowBeanDefinitionOverriding=false,就能控制Spring两个相同key的bean相同时抛异常
bindToSpringApplication
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
看代码意思简介明了,拿取Environment的数据,将spring.mian开头的参数绑定到SpringApplication上,所以SpringApplication上的参数可以通过yml配置
createApplicationContext
这个跟getOrCreateEnvironment方法一样,根据不同的WebApplicationType来确定启动不同的spirng容器
- SERVLET ->org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
- REACTIVE -> org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
- 默认 -> org.springframework.context.annotation.AnnotationConfigApplicationContext
prepareContext
代码如下
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//将常见的Environment 添加到Spring容器中
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
//广播ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans 将ApplicationArguments参数,spirngBootBanner添加到参数中
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置allowBeanDefinitionOverriding参数,此参数通过SpringBoot暴露给用户,此参数控制Spring两个相同key的bean是否覆盖
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//广播ApplicationPreparedEvent事件
listeners.contextLoaded(context);
}
总结
SpringBoot 再启动的时候,做了以下事情
- 启动计时器
- 初始化监听器 SpringApplicationRunListeners,里面只包含一个listener(EventPublishingRunListener)
- 广播 ApplicationStartingEvent事件
- 创建 DefaultApplicationArguments 此时这个类就是存放通过main函数传入的args
- 创建 ConfigurableEnvironment
- 配置 ConfigurableEnvironment,包含PropertySources ,Profiles
- 广播 ApplicationEnvironmentPreparedEvent事件
- 拿取ConfigurableEnvironment的数据,将spring.mian开头的参数绑定到SpringApplication上
- 打印Banner
- 创建 ConfigurableApplicationContext (spirng context)
- ConfigurableApplicationContext.set(ConfigurableEnvironment)
- 执行初始化的所有ApplicationContextInitializer
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
- 广播 ApplicationContextInitializedEvent 事件
- 将 DefaultApplicationArguments对象放入ConfigurableApplicationContext 的beanFactory 中
- 设置beanFactory的allowBeanDefinitionOverriding 属性
- 广播 ApplicationPreparedEvent事件
- 执行ConfigurableApplicationContext.refresh()刷新Spring上下文,此时Spring容器正式启动发挥作用,包括读Bean 还有AOP 等等
- 广播 ApplicationStartedEvent 事件
- 获取spring中所有 ApplicationRunner,CommandLineRunner 执行 run方法
- 广播 ApplicationReadyEvent 事件
各种广播其实就是SpringBoot还有Spring的各种扩展点,可以在SpringBoot启动到相应的阶段做些自己想做的事情