微服務中文介紹:https://www.cnblogs.com/liuning8023/p/4493156.html
Springboot中文學習文檔:http://felord.cn/_doc/_springboot/2.1.5.RELEASE/_book/
簡單閱覽下學習文檔,梳理下Springboot的使用功能:
- 外部化配置
1、yaml腳本存放位置的優先級;
2、spring.profiles.active多環境yaml配置;
3、@Value與@ConfigurationProperties獲取配置文件對應key的值(區別:哪個支持寬鬆綁定?支持表達式及表達式函數?);
4、@Validated+諸如@Email進行配置信息校驗;
5、新增banner.txt來更改啓動日誌Logo;
6、使用@Target@Retention@Documented自定義註解,使用@Aspect@Before進行aop分析;
- 開發web應用程序
1、熱部署插件JRebel(非免費)或spring-boot-devtools;
2、用@WebServlet@ServletComponentScan創建servlet加入springboot容器,運用ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean去裝載自定義servlet、fileter和listener;
3、thymeleaf的依賴配置與語法使用,及國際化配置(i18n);
4、數據源配置(含druid的依賴)與JDBCTemplate對數據庫數據的操作;
5、多數據源的動態切換(自定義註解 or 層級目錄兩種方式);
下面進入源碼學習的正題:(當前以最新SpringbootV2.4.4版本進行學習)
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
此處可以分爲兩個流程:new SpringApplication和run方法。
第一個步驟:new SpringApplication
- resourceLoader從註解來看是一個資源加載loader,這裏傳入的是null,應該後續會使用到默認值 。
- primarySources傳入的是我們main方法的啓動類,從這裏可以看出能傳入多個,這裏我們傳入的就一個 。
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
1、在new SpringApplication中,配置source、是否web環境等準備工作;
2、webApplicationType表示web服務類型,從這個枚舉類內部代碼可以看出有none,servlet,reactive三種,根據依賴包來進行判斷的;
3、創建初始化構造器和應用監聽器,使用this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class/ApplicationListener.class));
- ApplicationContextInitializer通過接口註解瞭解到:
①用於在ConfigurableApplicationContext執行refresh操作之前對它進行一些初始化操作;
②通常被用作web應用,在一些程序設計在spring容器初始化使用。
③支持Spring中的Ordered接口以及@Order註解來對多ApplicationContextInitializer實例進行排序,按照排序後的順序依次執行回調
- ApplicationListener基於觀察者模式實現的一種application程序啓動過程中觀察程序狀態過程變更的一種機制,可以自定義實現ApplicationListener用於觀察ApplicationEvent或者某種Event,甚至可以自定義Event,然後手動出發publishEvent。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
跟進一下SpringFactoriesLoader.loadFactoryNames 和 this.createSpringFactoriesInstances 兩個方法。發現創建 ApplicationContextInitializer 和 ApplicationListener 兩個類的初始化構造器,方便後續邏輯可以獲取構造器(工廠)對象。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
此處可以發現讀取了spring的默認配置spring.factories(jar:file:/C:/Users/Administrator/.m2/repository/org/springframework/boot/spring-boot/2.4.4/spring-boot-2.4.4.jar!/META-INF/spring.factories)
# Logging Systems
org.springframework.boot.logging.LoggingSystemFactory=\
org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
org.springframework.boot.logging.java.JavaLoggingSystem.Factory
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver
# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.context.config.ConfigDataNotFoundFailureAnalyzer,\
org.springframework.boot.context.properties.IncompatibleConfigurationFailureAnalyzer,\
org.springframework.boot.context.properties.NotConstructorBoundInjectionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer,\
org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer
# Failure Analysis Reporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
# DataSource Initializer Detectors
org.springframework.boot.jdbc.init.dependency.DataSourceInitializerDetector=\
org.springframework.boot.flyway.FlywayDataSourceInitializerDetector,\
org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializerDetector,\
org.springframework.boot.liquibase.LiquibaseDataSourceInitializerDetector,\
org.springframework.boot.orm.jpa.JpaDataSourceInitializerDetector
# Depends On DataSource Initialization Detectors
org.springframework.boot.jdbc.init.dependency.DependsOnDataSourceInitializationDetector=\
org.springframework.boot.jdbc.init.dependency.AnnotationDependsOnDataSourceInitializationDetector,\
org.springframework.boot.jdbc.SpringJdbcDependsOnDataSourceInitializationDetector,\
org.springframework.boot.jooq.JooqDependsOnDataSourceInitializationDetector,\
org.springframework.boot.orm.jpa.JpaDependsOnDataSourceInitializationDetector
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList(names.size());
Iterator var7 = names.iterator();
while(var7.hasNext()) {
String name = (String)var7.next();
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
4、 mainApplicationClass通過deduceMainApplicationClass方法獲取main啓動類(異常類堆棧的方式),這裏可以看出main啓動類和primarySources對應的類可以不一樣。 也就是爲什麼不直接用primarySources作爲啓動類的原因。deduceMainApplicationClass方法通過創建異常的方式獲取方發棧,然後逐級推斷出啓動類。
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
}
return null;
}
第二步 run() 方法
public ConfigurableApplicationContext run(String... args) {
//1、StopWatch簡單的看成一個stop watch的機制,保存stop的記錄信息。
//初始化一個計時器,並開始計時
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//初始化啓動上下文
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
//2、configureHeadlessProperty即配置headless模式,這種模式是一種系統缺少顯示設備、鍵盤和鼠標外設的情況模式。
this.configureHeadlessProperty();
//3、SpringApplicationListeners爲SpringApplicationRunListener接口實現集合(創建SpringApplicationRunListener初始化構造器)初始化監聽器列表
//可以理解這個接口就是在spring啓動整個過程都需要回調這些listener
//debug能發現,拿到了一個名爲EventPublishingRunListener(RunListener構造方法中關聯上了全部applicationListener),這個就是用來進行觸發publishEvent的被觀察者
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//啓動EventPublishingRunListener,從而過濾並啓動相關Listener
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//4、ConfigurableEnvironment爲配置環境對象,簡單理解所有的配置信息彙總在這個對象中
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//5、Banner就是我們常在控制檯輸出的畫面橫幅,可以使用圖片或者文本進行替換
Banner printedBanner = this.printBanner(environment);
//6、ConfigurableApplicationContext根據webApp…Type進行構造的上下文對象
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//7、接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執行所有的initializers邏輯,做初始化準備操作。
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//8、refreshContext,可以理解成容器初始化節點,將執行bean的創建和實例化。
this.refreshContext(context);
//9、afterRefresh,容器後處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現類並執行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啓動最後。
this.afterRefresh(context, applicationArguments);
//10、然後根據stopwatch打印出啓動時間
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
//11、這裏調用ApplicationRunner和CommandLineRunner的實現類
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
- StopWatch簡單的看成一個stop watch的機制,保存stop的記錄信息
- configureHeadlessProperty即配置headless模式,這種模式是一種系統缺少顯示設備、鍵盤和鼠標外設的情況模式
- SpringApplicationListeners爲SpringApplicationRunListener接口實現集合,可以理解這個接口就是在spring啓動整個過程都需要回調這些listener,debug能發現,拿到了一個名爲EventPublishingRunListener,這個就是用來進行觸發publishEvent的被觀察者。this.getSpringFactoriesInstances方法剛剛在第一步中也使用到了。根據spring.factories的配置返回對應實例EventPublishingRunListener。
Spring Boot定義的 SpringApplicationRunListener 實現類只有 EventPublishingRunListener ,主要是定義了其生命週期,並且在Spring Boot啓動過程中完成回調。如果自定義實現的話,則需要使用自動裝配的方式將自己添加到META-INF/spring.factories中才能被執行回調。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
--------> 此時,又多了一個RunListener(也是根據spring.factories中SpringApplicationRunListener配置的value值,之前SpringApplication已加載9個Listener和7個Initializer)<----------
-
listeners.starting(bootstrapContext, this.mainApplicationClass) 發佈springboot啓動事件,通過doWithListeners方法對事件進行發佈。
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
this.doWithListeners("spring.boot.application.starting", (listener) -> {
listener.starting(bootstrapContext);
}, (step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
解析doWithListeners方法:
1、初始化一個StartupStep實例,並命名爲"spring.boot.application.starting",其作用爲:追蹤“執行時間”或其他指標。
2、對內部維護的監聽器列表進行遍歷,執行傳入的listenerAction。由於listeners中只包含一個EventPublishingRunListener對象,故此處是用EventPublishingRunListener實例對象執行starting(bootstrapContext)方法。
下面將進入EventPublishingRunListener 的starting重寫方法
public void starting(ConfigurableBootstrapContext bootstrapContext) {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
public void multicastEvent(ApplicationEvent event) {
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
/**
* 批量消費事件:對符合要求的監聽器進行遍歷,執行invokeListener(listener, event)方法
*/
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
//遍歷匹配事件的監聽器
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = source != null ? source.getClass() : null;
AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
AbstractApplicationEventMulticaster.CachedListenerRetriever newRetriever = null;
AbstractApplicationEventMulticaster.CachedListenerRetriever existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);
if (existingRetriever == null && (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
newRetriever = new AbstractApplicationEventMulticaster.CachedListenerRetriever();
existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
if (existingRetriever != null) {
newRetriever = null;
}
}
if (existingRetriever != null) {
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
}
//匹配符合的Listeners,此處符合ApplicationStartingEvent事件的監聽器有:LoggingApplicationListener
//、BackgroundPreinitializer、DelegatingApplicationListener、LiquibaseServiceLocatorApplicationListener四個
return this.retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
下面跟進invokeListener(listener, event)方法,errorHandler爲空,直接執行doInvokeListener方法。
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass()) && (!(event instanceof PayloadApplicationEvent) || !this.matchesClassCastMessage(msg, ((PayloadApplicationEvent)event).getPayload().getClass()))) {
throw var6;
}
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(this.getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, var6);
}
}
}
當事件爲ApplicationStartingEvent時,以上4個監聽器的onApplicationEvent(event)方法彙總如下,它們分別進行了以下操作:
① LoggingApplicationListener初始化了loggingSystem
② BackgroundPreinitializer:未執行任何操作
③ DelegatingApplicationListener:未執行任何操作
④ LiquibaseServiceLocatorApplicationListener:未執行任何操作
/**
* 當listener爲LoggingApplicationListener時
*/
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
// 初始化loggingSystem
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
this.loggingSystem.beforeInitialize();
}
// ---------------------------------------------------------------
/**
* 當listener爲BackgroundPreinitializer時,由於未識別到ApplicationStartingEvent事件,故不執行任何操作
*/
public void onApplicationEvent(SpringApplicationEvent event) {
if (!ENABLED) {
return;
}
if (event instanceof ApplicationEnvironmentPreparedEvent
&& preinitializationStarted.compareAndSet(false, true)) {
performPreinitialization();
}
if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
&& preinitializationStarted.get()) {
try {
preinitializationComplete.await();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
/**
* performPreinitialization方法,執行各種初始化
**/
private void performPreinitialization() {
try {
Thread thread = new Thread(new Runnable() {
public void run() {
this.runSafely(new BackgroundPreinitializer.ConversionServiceInitializer());
this.runSafely(new BackgroundPreinitializer.ValidationInitializer());
this.runSafely(new BackgroundPreinitializer.MessageConverterInitializer());
this.runSafely(new BackgroundPreinitializer.JacksonInitializer());
this.runSafely(new BackgroundPreinitializer.CharsetInitializer());
BackgroundPreinitializer.preinitializationComplete.countDown();
}
public void runSafely(Runnable runnable) {
try {
runnable.run();
} catch (Throwable var3) {
}
}
}, "background-preinit");
thread.start();
} catch (Exception var2) {
preinitializationComplete.countDown();
}
}
// ---------------------------------------------------------------
/**
* 當listener爲DelegatingApplicationListener時,由於未識別到ApplicationStartingEvent事件,故不執行任何操作
*/
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
if (delegates.isEmpty()) {
return;
}
this.multicaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<ApplicationEvent> listener : delegates) {
this.multicaster.addApplicationListener(listener);
}
}
if (this.multicaster != null) {
this.multicaster.multicastEvent(event);
}
}
// ---------------------------------------------------------------
/**
* 當listener爲LiquibaseServiceLocatorApplicationListener時,由於LIQUIBASE_PRESENT爲false,故不執行任何操作
*/
public void onApplicationEvent(ApplicationStartingEvent event) {
if (LIQUIBASE_PRESENT) {
new LiquibasePresent().replaceServiceLocator();
}
}
彙總一下:
(一)new SpringApplication
- 配置source和web環境;
- 創建初始化構造器和應用監聽器;
- 配置應用的主方法所在類;
(二)run 第一部分
- 初始化計時、啓動上下文、設置系統參數;
- 初始化監聽器列表;
- 發佈springboot開始啓動事件starting;
SpringApplicationRunListener 屬於應用程序啓動層面的監聽器,在springboot啓動時候,調用run方法進行反射加載初始化。此時上下文還沒有加載,如果通過@Compnant是起不了作用的
ApplicationListener 通過spring上下文加載初始化的
剩下的部分將在下一章節,繼續學習:
- ConfigurableEnvironment爲配置環境對象,簡單理解所有的配置信息彙總在這個對象中
- Banner就是我們常在控制檯輸出的畫面橫幅,可以使用圖片或者文本進行替換
- ConfigurableApplicationContext根據webApp…Type進行構造的上下文對象
- 接下來進入關鍵步驟的第一步:prepareContext,準備容器階段,將執行所有的initializers邏輯,做初始化準備操作
- refreshContext,可以理解成容器初始化節點,將執行bean的創建和實例化
- afterRefresh,容器後處理, 可以看到會找到ApplicationRunner和CommandLineRunner的實現類並執行。但從2.x版本來看,似乎這個方法是個空方法,applicationRun和commandRun移到啓動最後。
- 然後根據stopwatch打印出啓動時間
- 這裏調用ApplicationRunner和CommandLineRunner的實現類