SpringBoot啓動流程分析
SpringBoot啓動流程主要分兩部分,一部分是在創建SpringApplication
的時候,一部分是在Run
方法裏
1.首先來看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));
// 1. 可能的web應用程序類型的類型。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 設置初始化應用context
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3.設置初始化監聽
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 4. 推演主程序類
this.mainApplicationClass = deduceMainApplicationClass();
}
1.可能的web應用程序類型的類型。
這段代碼是來推斷我們的應用是哪種web應用程序
2.設置初始化應用context
在設置初始化應用context的時候 ,是先執行了`getSpringFactoriesInstances(ApplicationContextInitializer.class)方法,參數是ApplicationContextInitializer.class字節碼對象
我們先來看看他是如何加載這些類的
先從緩存中拿,如果沒有從資源文件裏取
雙擊Shift搜索spring.factories可以看到它存在於以下工程中
從Map中根據org.springframework.context.ApplicationContextInitializer的類型拿到需要的類初始化類,斷點進入getOrDefault(factoryClassName, Collections.emptyList());方法,之後就是把加載到的類放到集合中備用
3.初始化監聽器類
和初始化應用context沒有什麼區別,
唯一不同的是getSpringFactoriesInstances(ApplicationListener.class))傳進去的是·ApplicationListener.class所以這裏就不再贅述。
4.推演主程序類
到這裏就完成了SpringBoot啓動過程中初始化SpringApplication的過程。
小結
SpringApplication的流程,大致可以分爲四個步驟:
推演web應用的類型(如果沒有加web依賴類型NONE)
初始化ApplicationContextInitializer
初始化ApplicationListener
推演出主程序類
通過這樣四個步驟就完成了第一步SpringApplication的初始化過程。
2.在來看Run方法都幹什麼了
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//計時器開始
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置Headless模式,是在缺少顯示屏、鍵盤或者鼠標時的系統配置
// 默認爲true
configureHeadlessProperty();
//獲取所有的監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
//啓動監聽器
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//準備環境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置忽略的bean
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//創建容器
context = createApplicationContext();
//異常相關處理
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//準本應用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器
refreshContext(context);
//刷新容器後的擴展接口
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//發佈監聽應用上下文啓動完成
listeners.started(context);
//執行runner
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;
}
下面就對關鍵步驟進行解釋
1.獲取所有的監聽器
這塊代碼就很熟悉了,它的主要作用就是去META-INFO/spring.properties中加載配置SpringApplicationRunListener的監聽器如下:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\norg.springframework.boot.context.event.EventPublishingRunListener
顯然只有一個事件發佈監聽器類,拿到了EventPublishingRunListener啓動事件發佈監聽器,下一步就是開始啓動了listeners.starting();我們往下跟源碼看
啓動的時候實際上是又創建了一個ApplicationStartingEvent對象,其實就是監聽應用啓動事件。
其中initialMulticaster是一個SimpleApplicationEventMulticaster
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//獲取線程池
Executor executor = getTaskExecutor();
//爲每一個監聽器創建一個線程
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
2.準備環境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置環境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//環境準備完成
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
3.配置忽略的Bean
4.打印Banner
這個是可以自定義的,也可以是圖篇或是文本文件中的圖形。
5.創建容器
我們的環境是servlet,DEFAULT_SERVLET_WEB_CONTEXT_CLASS其實servlet通過反射的方式創建對象
6.異常錯誤處理
其實還是去META-INFO/spring.factories配置文件中加載SpringBootExceptionReporter類
7.準備應用上下文
這裏就會根據之前創建的上下文、準備的環境、以及監聽等準備應用上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//設置環境參數
context.setEnvironment(environment);
//設置後處理應用上下文
postProcessApplicationContext(context);
//把從spring.properties中加載的org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,進行初始化操作
applyInitializers(context);
//發佈應用上下文事件
listeners.contextPrepared(context);
//打印啓動日誌
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//註冊一個名字是springApplicationArguments單例的bane
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//註冊一個名字是springBootBanner單例的bean
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
//是否允許類重名
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//是否可以懶加載
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
//獲取所有的資源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//創建BeanDefinitionLoader加載器加載所有的資源
load(context, sources.toArray(new Object[0]));
//發佈上下文事件
listeners.contextLoaded(context);
}
8.刷新應用上下文
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//準備刷新上下文
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//通知子類刷新內部工廠
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//準備Bean工廠
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 允許在上下文子類中對bean工廠進行後處理。
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//調用上下文中註冊爲bean的工廠處理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//註冊後置處理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化信息源
initMessageSource();
// Initialize event multicaster for this context.
//初始化上下文事件發佈器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//初始化其他自定義bean
onRefresh();
// Check for listener beans and register them.
//註冊監聽器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//完成bean工廠初始化
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//完成刷新,清緩存,初始化生命週期,事件發佈等
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
//銷燬Bean
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
在onRefresh()這個方法中去創建的Tomcat服務
創建了Tomcat對象,並設置參數
在finishRefresh方法中啓動tomcat服務
9.刷新後處理
afterRefresh()是個一空實現,留着後期擴展
10.發佈監聽應用啓動事件
調用context.publishEvent方法,發佈應用啓動事件ApplicationStartedEvent
11.執行Runner
獲取所有的ApplicationRunner和CommandLineRunner來初始化一些參數callRunner()是一個回調函數
12.發佈上下文準備完成的事件
這段代碼看上去似成相識,前面有很多類似的代碼,不同的是這裏上下文準備完成之後發佈了一個ApplicationReadyEvent事件,聲明一下應用上下文準備完成