SpringBoot是现在使用是如此的广泛,几乎所有的后端面试官都会就它的使用和基本原理方面进行考察。
现在就来盘一盘springboot的启动流程!
回答这个问题只需要查看SpringApplication.class就行了,下面截取了主要启动代码片段。
public ConfigurableApplicationContext run(String... args) {
...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 1. 准备环境变量
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 2. 准备上下文、主要是创建上下文和扩展点调用
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 3. 刷新上下文
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 这里调用了CommandLineRunner.class和ApplicationRunner.class所以我们写的Runner才能自动执行呢
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
...
return context;
}
复制代码
把各种渠道配置的变量都整合到Environment对象中,方便后面的使用。
springboot通过事件机制把属性配置进行了解耦,可以方便的扩展其他的属性配置方式。
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 首先创建Environment对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 根据main中输入的参数配置变量
configureEnvironment(environment,applicationArguments.getSourceArgs());
// 发送ApplicationEnvironmentPreparedEvent事件
// 他会通知其他监听者完成属性的配置
// 比如通知BootstrapApplicationListener完成springcloud配置文件的环境配置
// 通知ConfigFileApplicationListener完成application.yml文件的读取配置
listeners.environmentPrepared(environment);
// 绑定"spring.main"为当前的application
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
// 如果自定义Environment就转换成StandardEnvironment
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
复制代码
在容器初始化之前做一些工作,如ApplicationContextInitializer的调用,事件发送。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// 预处理,如果beanNameGenerator、resourceLoader不为空就将其添加到上下文中,并将ConversionService也添加到上下文中
postProcessApplicationContext(context);
// 调用所有的ApplicationContextInitializer,在刷新上下文之前做一些可能的工作。
applyInitializers(context);
// 发送ApplicationContextInitializedEvent事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
...
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
复制代码
刷新上下文完成ioc容器的初始化!
private void refreshContext(ConfigurableApplicationContext context) {
// 调用AbstractApplicationContext.refresh()完成ioc容器的初始化
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
复制代码