首先分享以前的全部文章 , 歡迎點贊收藏轉發三連下次必定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 備份 : 👉 gitee.com/antblack/ca…java
這一篇來講一說 SpringBoot Application 的主流程.react
SpringAppliation 的主流程入口很簡單 :git
@SpringBootApplication
public class BaseApplication {
public static void main(String[] args) {
SpringApplication.run(BaseApplication.class, args);
}
}
1 > 使用 @SpringBootApplication 註解,標明是 Spring Boot 應用。經過它,能夠開啓自動配置的功能。
2 > main 方法 : 調用 SpringApplication#run(Class<?>... primarySources) 方法,啓動 Spring Boot 應用
複製代碼
咱們從這個入口一步步看看 , 這個流程裏面到底作了什麼 , 其中主要涉及這幾件事:github
流程圖web
核心流程主要在SpringApplication.class 中 ,咱們從這個流程看看:spring
SpringApplication 屬性編程
F- resourceLoader
?- 資源加載器
F- primarySources
?- 主要的 Java Config 類的數組
F- webApplicationType
?- 調用 WebApplicationType#deduceFromClasspath() 方法,經過 classpath ,判斷 Web 應用類型。
F- listeners
?- ApplicationListener 數組。
F- mainApplicationClass
?- 調用 #deduceMainApplicationClass() 方法,得到是調用了哪一個 #main(String[] args) 方法
// 提供了三種 ApplicationContext 加載類 , 這個後續會用上
String DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext";
String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
// 默認Banner 地址 -> "banner.txt"
String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
複製代碼
SpringApplication run 方法主流程數組
M1_01- run()
T-> 計時 ,建立 StopWatch 而且啓動 , 用於記錄啓動的時長
-> configureHeadlessProperty() : 配置 headless 屬性
?- Headless模式是系統的一種配置模式。在該模式下,系統缺乏了顯示設備、鍵盤或鼠標
?- 該模式下能夠建立輕量級組件 , 收集字體等前置工做
- getRunListeners : 獲取 SpringApplicationRunListeners ,而且開啓監聽 listeners.starting()
- 1 建立 ApplicationArguments 對象
- 2 prepareEnvironment 加載屬性配置(傳入 listener + arguments ) -> M20_01
?- 執行完成後,全部的 environment 的屬性都會加載進來 (application.properties等)
- 3 打印banner(printBanner)
- 4 建立Spring 容器(createApplicationContext)
- 準備異常對象(getSpringFactoriesInstances.SpringBootExceptionReporter )
- 5 調用全部初始化類的 initialize 方法(prepareContext) , 初始化Spring 容器
- 6 刷新容器(refreshContext) , 執行 Spring 容器的初始化的後置邏輯(afterRefresh)
T-> 計時完成
- 7 通知 SpringApplicationRunListener , 執行異常處理等收尾
複製代碼
SpringApplication 主流程僞代碼markdown
public ConfigurableApplicationContext run(String... args) throws Exception {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 調用 M1_21 獲取 ConfigurableEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// PS:M1_01_03
context = createApplicationContext();
// 從 factories 中獲取 Exception M1_11
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// M1_35 : 爲 Context 添加屬性
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// M1_50 : 刷新容器Bean
refreshContext(context);
afterRefresh(context, applicationArguments);
// 計時結束
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 執行 implements ApplicationRunne 對象
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;
}
複製代碼
M1_21- prepareEnvironment
- getOrCreateEnvironment -> M21_01
-
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// 內部經過 WebApplicationType 生成不一樣的 Environment (能夠set 本身的 Environment) -> M1_23
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 重寫此方法以徹底控制環境自定義,或者重寫上述方法之一以分別對屬性源或概要文件進行細粒度控制。 -> M1_24
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 對 configurationProperties 屬性進行處理 -> M2_01
ConfigurationPropertySources.attach(environment);
// listener 處理
listeners.environmentPrepared(environment);
// 將 environment 綁定到 SpringApplication
bindToSpringApplication(environment); -> M1_25 if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
M1_23- getOrCreateEnvironment
- 經過 webApplicationType , 建立三種不一樣的 Environment
// M1_23 僞代碼
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
M1_24- configureEnvironment
- 獲取 ApplicationConversionService 的實現類
- 調用 configurePropertySources(environment, args) , 在此應用程序環境中添加、刪除或從新排序任何PropertySources。
- MutablePropertySources sources = environment.getPropertySources();
?- 獲取前面的 MutablePropertySources
- sources.addLast : 添加 defaultProperties
- 若是 args > 0 , 且能夠添加 commonLine , 則添加CommandLineProperties
?- 其中會判斷屬性 commandLineArgs 是否會存在 ,存在則經常使用替換方式
// M1_24 僞代碼
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
C2- ConfigurationPropertySources.
M2_01- attach(environment);
- sources = ((ConfigurableEnvironment) environment).getPropertySources() : 獲取 MutablePropertySources
- attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME) : 獲取 PropertySource
- 若是 attached 爲null 或者 不等於 sources , 則將 sources 替換原先的 attached
// Binder : 從一個或多個容器綁定對象的容器對象
M1_25- bindToSpringApplication
C3- Binder
M3_01- bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,boolean allowRecursiveBinding, boolean create)
- context.clearConfigurationProperty() : 清空原屬性
- handler.onStart(name, target, context) : BindHandler 開始綁定
- bindObject : 將 屬性綁定到對象
- handleBindResult : 返回 綁定結果
private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,boolean allowRecursiveBinding, boolean create) {
try {
Bindable<T> replacementTarget = handler.onStart(name, target, context);
if (replacementTarget == null) {
return handleBindResult(name, target, handler, context, null, create);
}
target = replacementTarget;
Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
return handleBindResult(name, target, handler, context, bound, create);
}catch (Exception ex) {
return handleBindError(name, target, handler, context, ex);
}
}
複製代碼
configureIgnoreBeanInfo 配置併發
// 其中僅設置了2個屬性 :
// 屬性一 : spring.beaninfo.ignore , 用於
environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE)
// 設置二 : 設置到 System 中
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString())
//那麼該屬性是爲了幹什麼 ? -> spring.beaninfo.ignore
當值爲 true 時 , 意味着跳過對 BeanInfo 類的搜索 .
若是經歷了對不存在的 BeanInfo 類的重複 ClassLoader 訪問,能夠考慮將這個標誌切換爲「 true」,以防在啓動或延遲加載時這種訪問開銷很大
可是現階段全部 BeanInfo 元數據類,默認值是"false"
複製代碼
純粹是好奇 , 過來看一眼
M1_28- printBanner : 準備 Banner 類
// 代碼詳情
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null)
? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
// 核心代碼 -> 調用類 SpringBootBanner
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
// 核心類 SpringBootBanner
C- SpringBootBanner
M- printBanner
- printStream.println(line) : 逐行打印那個 Spring
- 打印版本號
?- :: Spring Boot :: (v2.3.1.RELEASE)
// ps : 點進去就知道了 , 逐行打印
複製代碼
該邏輯爲建立 ApplicationContext 的相關邏輯 , 這裏先簡單過一下 :
C- SpringApplication
M1_30- createApplicationContext
- Class<?> contextClass = this.applicationContextClass;
IF- contextClass爲null
- 根據 webApplicationType 類型,得到 ApplicationContext 類型
- AnnotationConfigServletWebServerApplicationContext
- AnnotationConfigApplicationContext
- AnnotationConfigReactiveWebServerApplicationContext
- 根據 contextClass 建立 ApplicationContext 對象
// M1_30 僞代碼
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根據不一樣 webApplicationType 準備不一樣的 ContextClass
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
// 反射構造器方法得到 context 實現類
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
// ApplicationContext 核心流程值得深刻 , 後面開一個單章來詳細是說
複製代碼
PS:M1_01_03 生成一個 ConfigurableApplicationContext
PS : ApplicationContext 體系結構
這裏補充一下 Reactive 的區別
如圖所示 , Reactive 是 Spring 中一個重要的技術棧 , Reactive 能夠用於構建響應性、彈性、彈性和消息驅動的企業級反應系統.
WebFlux 並非 Spring MVC 替代,它主要應用仍是在異步非阻塞編程模型上 , 使用了 WebFlux 的應用,其總體響應時間更短,啓動的線程數更少,使用的內存資源更少。同時,延遲越大,WebFlux 的優點越明顯。
具體能夠參考這篇文檔 @ blog.csdn.net/u010862794/…
M1_11 獲取 SpringBootExceptionReporter 的處理類
getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);
// Spring.factories
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
複製代碼
M1_12 callRunners : 幹什麼
該方法主要是運行 ApplicationRunner
// callRunners 主流程
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);
}
}
}
@Component
public class SourceTestLogic implements ApplicationRunner {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("------> run <-------");
}
}
// 簡單點說 , ApplicationRunner 初始化啓動就是這裏作的
複製代碼
Context 的二次處理分爲 三步 :
prepareContext 的主流程
// prepareContext(context, environment, listeners, applicationArguments, printedBanner);
M1_35- prepareContext : 準備 ApplicationContext 對象,主要是初始化它的一些屬性
-> 1 設置 context 的 environment 屬性
-> 2 調用 #postProcessApplicationContext(ConfigurableApplicationContext context) 方法,
?- 設置 context 的一些屬性 -> M1_36
-> 3 調用 #applyInitializers(ConfigurableApplicationContext context) 方法,
?- 初始化 ApplicationContextInitializer -> applyInitializers
-> 4 調用 SpringApplicationRunListeners#contextPrepared
?- 通知 SpringApplicationRunListener 的數組,Spring 容器準備完成
-> 5 設置 beanFactory 的屬性
-> 6 調用 #load(ApplicationContext context, Object[] sources) 方法,加載 BeanDefinition 們
?-
-> 建立 BeanDefinitionRegistry 對象
-> 設置 loader 屬性
-> 執行BeanDefine 加載
// M1_35 僞代碼
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 爲容器設置 environment
context.setEnvironment(environment);
// 設置容器 classloader 和 conversionService , 即容器中類的加載工具
postProcessApplicationContext(context);
// 在刷新上下文以前,將任何ApplicationContextInitializers應用於該上下文
applyInitializers(context);
// listeners 執行 , 在建立並準備好ApplicationContext以後調用,但在加載源以前調用。
listeners.contextPrepared(context);
if (this.logStartupInfo) {
// 打印啓動日誌配置
logStartupInfo(context.getParent() == null);
// 打印 active profile 信息
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// beanFactory 注入相關Bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
// 添加一個新的BeanFactoryPostProcessor
// 該新的BeanFactoryPostProcessor將在刷新以前應用於此應用程序上下文的內部Bean工廠,而後再評估任何Bean定義。
// 在上下文配置期間調用。
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// -> M1_38
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
M1_36- postProcessApplicationContext : 在ApplicationContext中應用任何相關的後處理
- beanNameGenerator 存在則設置到 context.getBeanFactory().registerSingleton 中
- resourceLoader 存在且 爲 GenericApplicationContext類型 , 則 setResourceLoader
- resourceLoader 存在且 爲 resourceLoader 則 setClassLoader
- addConversionService 爲 true 則設置到 BeanFactory 中
// M1_36 僞代碼
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
M1_37- applyInitializers
FOR- (ApplicationContextInitializer initializer : getInitializers()) : for 循環處理 Initializers
- Assert判斷 是否爲 ApplicationContextInitializer 的實例
- initializer.initialize(context) 初始化對象
M1_38- load
- 這裏主要是建立 BeanDefinitionLoader protected void load(ApplicationContext context, Object[] sources) {
// 建立 BeanDefinitionLoader , 直接 new 的
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
// beanName 生成類
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// -> M5_01
loader.load();
}
C5- BeanDefinitionLoader
M5_01- loader
?- 這裏會循環調用 load source
// M5_01 僞代碼
for (Object source : this.sources) {
count += load(source);
}
複製代碼
refresh 流程
C- AbstractApplicationContext
M1_50- refreshContext(context);
- refresh(context);
- 若是有 ShutdownHook (關閉鉤子) , 則註冊 registerShutdownHook
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 準備此上下文以進行刷新。
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 告訴子類刷新內部bean工廠
prepareBeanFactory(beanFactory);
try {
// 容許在上下文子類中對bean工廠進行後處理
postProcessBeanFactory(beanFactory);
// 調用在上下文中註冊爲bean的工廠處理器
// IOC 的主要邏輯就在其中
invokeBeanFactoryPostProcessors(beanFactory);
// 註冊攔截Bean建立的Bean處理器
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的消息源
initMessageSource();
// 初始化此上下文的事件多主控器.
initApplicationEventMulticaster();
// 初始化特定上下文子類中的其餘特殊bean.
onRefresh();
// 檢查偵聽器bean並註冊它們.
registerListeners();
// 實例化全部剩餘的(非lazy-init)單例.
finishBeanFactoryInitialization(beanFactory);
//最後一步:發佈相應的事件
finishRefresh();
}catch (BeansException ex) {
// 銷燬已建立的單件以免資源懸空
destroyBeans();
// 重置「活動」標誌.
cancelRefresh(ex);
// 將異常傳播到調用方.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
// 這一部分也是 AbstractApplicationContext 的主要流程 , 放在後續 ApplicationContext 單章說
複製代碼
afterRefresh 流程
PS : 這是一個空實現
M1_60- afterRefresh
複製代碼
Application 啓動時 , Listener 總共涉及到四個操做 :
以我這微薄的英語水平 , 這裏面怕是有個進行時和一個過去時 , 哈哈哈哈哈
能夠看到 , 第一步和第四步目的都比較明確 , 主要來看看第二 . 三步
getRunListeners
C- SpringApplication
M- getRunListeners
- getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)
// 能夠看到 , 從 Factories 中獲取的 , 先階段只有一個 EventPublishingRunListener
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
複製代碼
listener.starting
//這裏的 starting 是 EventPublishingRunListener 運行
// 具體事件的處理邏輯 , 咱們後續文檔繼續深刻
C- EventPublishingRunListener
M- starting()
- this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
C- AbstractApplicationEventMulticaster
?- 將全部事件多播給全部註冊的偵聽器,並在調用線程中調用它們 , 簡單點說就是事件羣發
複製代碼
listeners.started(context)
// 這裏的事件就更多了
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
這裏仍是 EventPublishingRunListener
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
複製代碼
listener.running
// 發佈 ReadyEvent
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
複製代碼
篇幅有限 , 就不詳細看看哪些執行了
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
// 處理退出碼
handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
}finally {
reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
}catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
複製代碼
這一篇對 SpringBoot 的大概流程簡單過了一遍 , 篇幅有限有幾個點暫時先沒歸入 , 後續補充