SpringBoot 啟動流程原始碼筆記
阿新 • • 發佈:2019-02-11
springboot啟動一個專案程式碼例子如下
SpringApplication.run(xxxx.class, args);
跟蹤進去如下
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
上面是建立了一個SpringApplication物件,然後呼叫其run方法
SpringApplication構造方法:
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//上面傳null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//上面傳執行的主方法的類
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//三種類型:REACTIVE|NONE|SERVLET;REACTIVE對應spring5新增的webflux;SERVLET是傳統servlet
this.webApplicationType = deduceWebApplicationType();
//從META-INF/spring.factories檔案獲取ApplicationContextInitializer型別的類
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//從META-INF/spring.factories檔案獲取ApplicationListener型別的類
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//獲取執行的主方法的類
this.mainApplicationClass = deduceMainApplicationClass();
}
建立了SpringApplication物件後,接著執行它的run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();//效能監控
//Spring容器類
ConfigurableApplicationContext context = null;
//SpringBootExceptionReporter類
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();//設定系統的“java.awt.headless”屬性
//從META-INF/spring.factories中獲得SpringApplicationRunListener型別的類,放進SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();//開始事件
try {
//命令列啟動的引數封裝到DefaultApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//【標記1】例項或配置好environment
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//對系統中的“spring.beaninfo.ignore”屬性進行處理
configureIgnoreBeanInfo(environment);
//列印招牌
Banner printedBanner = printBanner(environment);
//【標記2】建立容器,三種類型
context = createApplicationContext();
//從META-INF/spring.factories中獲取SpringBootExceptionReporter型別的類
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//【標記3】準備容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//重新整理容器
refreshContext(context);
//留給子類實現
afterRefresh(context, applicationArguments);
//效能監控停止
stopWatch.stop();
//列印資訊,主要關於啟動時間
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//監聽器回撥started
listeners.started(context);
//執行ApplicationRunner、CommandLineRunner(還沒看懂怎麼啟動的)
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//會回撥listener的failed方法
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//監聽器回撥running方法
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
上面的springboot的啟動流程一目瞭然了,準備好環境environment,準備好容器context,然後載入好各種資源到容器中,接著啟動。
【標記1】例項或配置好environment
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 建立Environment型別物件,三種:StandardServletEnvironment|StandardReactiveWebEnvironment|StandardEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置Environment的一些屬性
configureEnvironment(environment, applicationArguments.getSourceArgs());
//監聽器回撥environmentPrepared方法
listeners.environmentPrepared(environment);
//繫結environment(其實還沒看懂幹嘛)
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
//這裡再次檢測是否需要把environment轉為對應型別的environment,上面getOrCreateEnvironment裡不是判斷過了嗎??
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
//這裡把environment中的propertySources成員中的configurationProperties鍵對應的值替換為SpringConfigurationPropertySources
ConfigurationPropertySources.attach(environment);
return environment;
}
【標記2】建立容器,三種類型
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch...
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
【標記3】準備容器
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//設定environment
context.setEnvironment(environment);
//設定bean名字生成器、resourceloader、classloader、ApplicationConversionService
postProcessApplicationContext(context);
//執行從META-INF/spring.factories中獲取的ApplicationContextInitializer型別的initialize方法
applyInitializers(context);
//監聽器回撥contextPrepared方法,容器已經準備好
listeners.contextPrepared(context);
//列印資訊:主要關於active profile的
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//向beanFactory註冊了bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//設定是否允許覆蓋bean
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//開始載入資源
load(context, sources.toArray(new Object[0]));
//監聽器回撥contextLoaded,容器已經載入完
listeners.contextLoaded(context);
}
prepareContext方法中會執行ApplicationContextInitializer的initialize方法,執行完的時候,當prepareContext方法執行完容器已載入好了資源
總結一下SpringApplicationRunListener的回撥順序
SpringApplicationRunListener共7個方法,回撥順序為:
starting -> environmentPrepared -> contextPrepared -> contextLoaded -> started -> running
failed方法在running前執行有錯誤發現才回調
對應事件為:
ApplicationStartingEvent -> ApplicationEnvironmentPreparedEvent -> ApplicationContextInitializedEvent -> ApplicationPreparedEvent -> ApplicationStartedEvent -> ApplicationReadyEvent