一、SpringApplication 是什麼?
Spring 應用的啓動類。
二、SpringApplication 執行了什麼?
-
創建 ApplicationContext 實例
ApplicationContext 就是我們所說的容器實例。
-
註冊 CommandLinePropertySource
CommandLinePropertySource 的作用是將命令行參數輸出爲 Spring 屬性。
-
刷新 ApplicationContext
這一步驟包括諸多操作,並且會加載所有的單例 bean。
-
觸發 CommandLineRunner bean
CommandLineRunner 是一個接口,它只有一個 run() 方法。
凡是實現了此接口的類,如果被加載進容器,就會執行其 run() 方法。
容器中可以包含多個實現 CommandLineRunner 的 bean,執行順序可以遵從 Ordered 接口或者 @Order 註解設置。
三、bean 加載源
SpringApplication 有諸多 bean 加載源:
-
AnnotatedBeanDefinitionReader
顧名思義,註解 bean 定義讀取。
-
XmlBeanDefinitionReader
xml 配置資源讀取。
-
ClassPathBeanDefinitionScanner
classpath 路徑掃描。
-
GroovyBeanDefinitionReader
... ...
四、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));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
1、resourceLoader
參數可以爲 null,爲 null 時,使用默認:
(this.resourceLoader != null) ? this.resourceLoader: new DefaultResourceLoader(null);
2、primarySources
主要的 bean 定義來源。
3、webApplicationType
web 應用類型判斷:
-
NONE:應用不會以 web 應用運行,且不會啓動內嵌 web 服務器。
-
SERVLET:基於 servlet web 應用,運行於內嵌 web 服務器。
-
REACTIVE:響應式 web 應用,運行於內嵌 web 服務器。
4、bootstrapRegistryInitializers
BootstrapRegistryInitializer:回調接口,用於 BootstrapRegistry 初始化。
BootstrapRegistry:對象註冊器,作用期間爲從應用啓動,Environment 處理直到 ApplicationContext 完備。
5、setInitializers
ApplicationContextInitializer 列表設置。
ApplicationContextInitializer:回調接口,用於 Spring ConfigurableApplicationContext 初始化。
通常用於 web 應用 ApplicationContext 自定義初始化。如註冊 property source、激活 profile 等。
6、setListeners
ApplicationListener 列表設置。
ApplicationListener:應用事件監聽接口,基於標準的 EventListener 接口,觀察者模式實現。
7、mainApplicationClass
main class
五、SpringApplication.run() 邏輯
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
創建,刷新 ApplicationContext 並運行 Spring 應用。
1、startTime
使用 System.nanoTime(),計算耗時間隔更精確。不可用於獲取具體時刻。
2、創建啓動上下文
DefaultBootstrapContext = createBootstrapContext();
BootstrapContext:啓動上下文,生命週期同 BootstrapRegistry。
DefaultBootstrapContext 繼承了 BootstrapContext、BootstrapRegistry。
用於 BootstrapRegistry 初始化。
3、ConfigurableApplicationContext
可配置的 ApplicationContext。
4、獲取事件監聽器
SpringApplicationRunListeners = getRunListeners()。
Spring 應用運行期間事件監聽。
5、starting 事件
listeners.starting():starting step。
6、啓動參數處理
ApplicationArguments:提供 SpringApplication 啓動參數訪問。
7、應用環境配置
ConfigurableEnvironment = prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
-
configureEnvironment() 模板方法,代理執行 configurePropertySources() 及 configureProfiles() 方法。
configurePropertySources():PropertySource 配置,用於添加、移除或者調序 PropertySource 資源。CommandLinePropertySource 在這一步處理。
configureProfiles():應用 profile 設置。
-
ConfigurationPropertySources.attach(environment)
ConfigurationPropertySources:提供對 ConfigurationPropertySource 的訪問。
attach(environment):就是將這個功能提供給 environment。
-
listeners.environmentPrepared(bootstrapContext, environment)
environment-prepared step。
-
DefaultPropertiesPropertySource.moveToEnd(environment)
DefaultPropertiesPropertySource:是一個 MapPropertySource,包含 SpringApplication 可以使用的一些默認屬性。爲了使用方便,默認會置於尾序。
-
bindToSpringApplication(environment)
將 environment 綁定到 SpringApplication。
Binder:用於對象綁定的容器。
8、configureIgnoreBeanInfo()
9、打印 Banner
printBanner()。
10、創建 ApplicationContext
createApplicationContext()。
內部通過 ApplicationContextFactory 創建。
ApplicationContextFactory:策略接口,默認實現爲 DefaultApplicationContextFactory。
11、ApplicationStartup 設置
爲容器設置 ApplicationStartup,用於記錄啓動過程性能指標。
12、ApplicationContext 準備
prepareContext()
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
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
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
-
設置環境
-
postProcessApplicationContext() 前置處理
beanNameGenerator 設置,用於 bean 名稱生成。
resourceLoader 設置,用於資源加載。
addConversionService:ConversionService 類型轉換 Service。
-
applyInitializers()
ApplicationContextInitializer 應用
-
contextPrepared 事件
【spring.boot.application.context-prepared】step
-
BootstrapContext 關閉
-
註冊 springApplicationArguments bean
-
註冊 springBootBanner bean
-
AbstractAutowireCapableBeanFactory
設置是否允許 bean 之間的循環依賴,並自動處理,默認爲 true。
設置是否允許 bean 定義覆蓋,默認爲 true。
-
lazyInitialization 懶加載
設置 LazyInitializationBeanFactoryPostProcessor post-processor。
-
PropertySource 重排序
設置 PropertySourceOrderingBeanFactoryPostProcessor post-processor。
-
getAllSources() bean 定義源加載
-
load() bean 定義加載,BeanDefinitionLoader
用於從底層資源加載 bean 定義信息,包括 xml、JavaConfig。
是基於 AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、ClassPathBeanDefinitionScanner 的門面模式。
beanNameGenerator、resourceLoader、environment 設置。
資源加載:
private void load(Object source) { Assert.notNull(source, "Source must not be null"); if (source instanceof Class<?>) { load((Class<?>) source); return; } if (source instanceof Resource) { load((Resource) source); return; } if (source instanceof Package) { load((Package) source); return; } if (source instanceof CharSequence) { load((CharSequence) source); return; } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }
-
contextLoaded() contextLoaded 事件
【spring.boot.application.context-loaded】step。
13、ApplicationContext 刷新
refreshContext()
註冊 shutdownHook。
Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
刷新操作:加載或刷新
AbstractApplicationContext::refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 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.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
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.
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();
contextRefresh.end();
}
}
}
作爲啓動方法,如果失敗,則必須銷燬所有已創建的單例bean。
-
StartupStep【spring.context.refresh】
-
準備刷新 prepareRefresh()
設置啓動日期。
設置 active 標誌。
initPropertySources():子類實現 PropertySource 初始化。
validateRequiredProperties():校驗 ConfigurablePropertyResolver#setRequiredProperties 設置的必需屬性。
obtainFreshBeanFactory():通過子類獲取最新的內部 bean factory。如果存在舊的則先銷燬,然後再創建新的返回。
-
prepareBeanFactory() 準備 bean factory
setBeanClassLoader():默認爲線程上下文類加載器,用於 bean 定義加載。
setBeanExpressionResolver() spel 表達式解析設置:StandardBeanExpressionResolver。
addPropertyEditorRegistrar():ResourceEditorRegistrar 用於 bean 創建過程。
添加 ApplicationContextAwareProcessor post-processor。
註冊依賴:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext。
添加 ApplicationListenerDetector post-processor:用於檢測發現實現了 ApplicationListener 的 bean。
LoadTimeWeaver 處理。
environment、systemProperties、systemEnvironment、applicationStartup 註冊。
-
postProcessBeanFactory():用於子類實現,修改內部 bean factory。
這一時期,所有的 bean 定義都已被加載,但還未實例化。
-
StartupStep【spring.context.beans.post-process】
-
invokeBeanFactoryPostProcessors() 觸發所有已註冊的 BeanFactoryPostProcessor
-
registerBeanPostProcessors() 註冊 bean post-processor
-
StartupStep【spring.context.beans.post-process】 結束
-
initMessageSource() MessageSource 初始化
容器內 bean 名稱:messageSource。
存在則檢查並設置 ParentMessageSource。
不存在則創建默認 DelegatingMessageSource,設置 ParentMessageSource 並註冊。
-
initApplicationEventMulticaster() 事件分發初始化
容器 bean:applicationEventMulticaster。ApplicationEventMulticaster 接口,用於管理 ApplicationListener,並執行事件分發。
不存在則創建並註冊 SimpleApplicationEventMulticaster 對象。
-
onRefresh()
用於子類初始化一些特有的 bean。
模板方法,用於重寫實現刷新邏輯。
-
registerListeners() 監聽器註冊
將實現了 ApplicationListener 接口的 bean 註冊到容器。
-
finishBeanFactoryInitialization() 實例化所有餘下的單例 bean。
conversionService。
註冊內嵌值(${...})解析器。
初始化 LoadTimeWeaverAware。
停用類型匹配 ClassLoader。
freezeConfiguration() 凍結所有的 bean 定義。所有註冊的 bean 定義都不允許再有變更。
preInstantiateSingletons() 實例化所有餘下的單例 bean。
14、afterRefresh()
ApplicationContext 刷新完畢後調用。
15、StartupInfoLogger
記錄應用啓動信息。
16、started 事件
listeners.started()
17、Runner 調用
包括 ApplicationRunner 和 CommandLineRunner。
18 ready 事件
listeners.ready()