SpringBoot 启动过程
SpringBoot 主要分为两大步:1、执行SpringApplication 构造;2、执行 run() 方法
1.SpringApplication 构造
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));
//推断应用类型:非 Web 应用;Web 应用,是 Servlet 类型的还是 Reactive 类型的
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//ApplicationContext 初始化器,对 ApplicationContext 做扩展
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取监听器,对SpringBoot在执行 run() 时发布的事件进行监听和处理
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断主类,获得当前 SpringBoot main 方法所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
- 获取 Bean Definition 源,可以通过解析 xml 文件或配置类等
- 推断应用类型:非 Web 应用;Web 应用,是 Servlet 类型的还是 Reactive 类型的
- ApplicationContext 初始化器,对 ApplicationContext 做扩展
- 获取监听器,对SpringBoot在执行 run() 时发布的事件进行监听和处理
- 推断主类,获得当前 SpringBoot main 方法所在的类
2.执行 run 方法源码
主要分为 12 个步骤和 7 大事件
run 方法源码:
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
//1.读取spring factories的配置信息,找到一个事件发布器的实现类,得到事件发布器,他是一个发布器,不是监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布 starting() 事件,表示spring Boot 程序开始启动
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//2.将main方法的args参数封装成ApplicationArguments对象,该对象有两类:一类是以“-”号开头的选项参数
//第二类是:非选型参数;选项参数可以通过命令行添加到environment中
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//3-6 步见prepareEnvironment()方法
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext,
applicationArguments);
configureIgnoreBeanInfo(environment);
//7.打印SpringBoot启动时的图标,也就是Banner信息
Banner printedBanner = printBanner(environment);
//8.创建Spring容器,根据上面提到的SpringApplication的构造方法中推断出的容器类型,在三种里选择一种实现
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//9-10步见prepareContext()方法
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments,
printedBanner);
//11.调用ApplicationContext的refresh方法,在refresh中就会调用各种BeanFactory的后处理器
//(BeanFactoryPostProcessor)、Bean的后处理器(BeanPostProcessor),初始化单例Bean
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(),
timeTakenToStartup);
}
//发布启动完成事件,表示SpringApplicationContext准备完毕
listeners.started(context, timeTakenToStartup);
//第12步见callRunners()方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//如果中间出现异常,在该方法中发布启动失败事件
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
//发布springBoot程序启动完成事件
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
prepareEnvironment 方法源码:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
//3.创建 environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//并将前面获取的选项参数信息封装成PropertySources,添加到environment中
configureEnvironment(environment, applicationArguments.getSourceArgs());
//4.将environment中不同风格的键(如:有的是“_”分隔的,驼峰的,以“-“分隔的)同一转化成”-“分隔的
ConfigurationPropertySources.attach(environment);
//5.发布environment准备完毕事件,然后spring factories中的监听器对象,该对象会调用environment的postProcessor
//对environment做扩展增强,添加更多的源(如:产生随机数的源、application.properties的源)
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
//6.把environment中所有以spring.main为前缀的键,跟springApplication对象做一个绑定
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
prepareContext源码:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext
context,ConfigurableEnvironment environment, SpringApplicationRunListeners
listeners,ApplicationArguments applicationArguments,
Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
//9.应用初始化器,对ApplicationContext进行功能增强
applyInitializers(context);
//发布容器准备完毕事件
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory)
.setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// Load the sources
//10.得到所有的BeanDefinition源(如:从xml中读取Bean、从配置类中获取、从组件扫描中获取)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//load方法将BeanDefinition加载到容器
load(context, sources.toArray(new Object[0]));
//发布容器加载完成事件
listeners.contextLoaded(context);
}
callRunners源码:
//12.调用所有实现了ApplicationRunner、CommandLineRunner接口的Bean
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
综上,run方法执行步骤为 12 步骤和 7 大事件:
- 读取spring factories的配置信息,找到一个事件发布器的实现类,得到事件发布器,他是一个发布器,不是监听器
- 发布 starting() 事件,表示spring Boot 程序开始启动
- 将main方法的args参数封装成ApplicationArguments对象,该对象有两类:一类是以“-”号开头的选项参数,第二类是:非选型参数;选项参数可以通过命令行添加到environment中
- 创建 environment ,并将前面获取的选项参数信息封装成PropertySources,添加到environment中
- 将environment中不同风格的键(如:有的是“_”分隔的,驼峰的,以“-“分隔的)同一转化成”-“分隔的
- 发布environment准备完毕事件,然后spring factories中的监听器对象,该对象会调用environment的postProcessor对environment做扩展增强,添加更多的源(如:产生随机数的源、application.properties的源)
- 把environment中所有以spring.main为前缀的键,跟springApplication对象做一个绑定
- 打印SpringBoot启动时的图标,也就是Banner信息
- 创建Spring容器,根据上面提到的SpringApplication的构造方法中推断出的容器类型,在三种里选择一种实现
- 应用初始化器,对ApplicationContext进行功能增强
- 发布容器准备完毕事件
- 得到所有的BeanDefinition源(如:从xml中读取Bean、从配置类中获取、从组件扫描中获取),load方法将BeanDefinition加载到容器
- 发布容器加载完成事件
- 调用ApplicationContext的refresh方法,在refresh中就会调用各种BeanFactory的后处理器(BeanFactoryPostProcessor)、Bean的后处理器(BeanPostProcessor),初始化单例Bean
- 发布启动完成事件,表示SpringApplicationContext准备完毕
- 调用所有实现了ApplicationRunner、CommandLineRunner接口的Bean
- 发布springBoot程序启动完成事件
- 如果中间出现异常,在handleRunFailure() 方法中发布启动失败事件