Spring Boot 启动分析
2020-12-08 本文已影响0人
月亮的颜色
以常见的SpringBoot 工程启动类举例
@SpringBootApplication(scanBasePackages = {"com.xxx.xxx.xxx"})
@EnableAspectJAutoProxy
@EnableConfigCenter
@EnableMbean
@EnableScheduling
public class FcFundingApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(FcFundingApplication.class, args);
Runtime.getRuntime().addShutdownHook(new AppShutdownHook());
}
}
这里简单的通过 SpringApplication#run 就可以启动Spring 容器, 并让其运行在 Web 容器中;
下面是其核心源码
public ConfigurableApplicationContext run(String... args) {
// 1. 计数器初始化并启动
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 2. 加载并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 3. 初始化环境变量
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 4. 创建上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 5. 准备上下文 1. 初始化容器 2. 加载容器资源
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 6. 刷新上下文
refreshContext(context);
// 7. 后置刷新上下文
afterRefresh(context, applicationArguments);
// 8. 停止计数器
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
其中如下图所示正常流程分成七个不同的阶段:
- starting : 监听器加载后, 通知启动中;
- environmentPrepared: 加载完环境变量
- contextPrepared: 应用完上下文初始化器
- contextLoaded : 加载完成spring的bean
- started: 刷新完容器,已经初始化完所有的容器
- runding: 前五步没有任何异常
- failed: 前五步有异常
其中比较复杂的是启动上下文的流程:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// 1. 配置 beanNameGenerator、 resourceLoader、 addConversionService
postProcessApplicationContext(context);
// 2. 初始化上下文, 并通知上下文准备完毕;
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 3. 注册启动相关的bean: springApplicationArguments、springBootBanner、LazyInitializationBeanFactoryPostProcessor
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 根据懒加载过滤器决定, 过滤掉某些单例bean, 使得其在启动后在加载;
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 4. 加载资源(Bean元数据)并会输出到容器中, 并通知容器加载完毕
// 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);
}
总结
SpringApplication#run 方法主要负责容器环境准备过程中的7个不同阶段;