在這篇文章中,我們接着上一篇的內容接着分析。
public ConfigurableApplicationContext run(String... args) {
//啓動應用的檢測
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//SpringBoot的上下文
ConfigurableApplicationContext context = null;
//失敗分析報告
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//SpringBoot的runlistener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//參數解析
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//配置環境變量
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//輸出Banner信息
Banner printedBanner = printBanner(environment);
//創建應用上下文
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//refresh上下文之前的準備
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
SpringApplication中的run方法的內容如上所示,上面就是整個SpringBoot應用啓動的主要調用方法,run方法中的參數即是我們的應用參數。下面我們來簡單的分析一下這個啓動過程。
StopWatch主要是監控啓動過程,統計啓動時間,檢測應用是否已經啓動或者停止。
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
通過在上一篇文章中的問題,對於getSpringFactoriesInstances這個方法你應該不陌生來吧。這裏也是從META-INF/spring.factories中獲取類型爲org.springframework.boot.SpringApplicationRunListener的配置值,這個默認的配置值爲:org.springframework.boot.context.event.EventPublishingRunListener。我們進入到EventPublishingRunListener這個類看一下它的構造函數
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
//創建一個SimpleApplicationEventMulticaster
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//把之前在SpringApplication中獲取到的listener循環放入到SimpleApplicationEventMulticaster中
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
通過上面的分析,我們可以看到EventPublishingRunListener把SpringApplication中的監聽器,都放到了SimpleApplicationEventMulticaster中,進行了統一的管理。listeners.starting();啓動事件監聽,這裏以後我們單獨開章節詳細說明.
//創建應用參數解析器
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
我們看一下DefaultApplicationArguments的構造函數的內容:
public DefaultApplicationArguments(String[] args) {
//首先判斷不能爲null,這裏大家可以想一下可變參數如果不傳值的話看看是什麼內容
Assert.notNull(args, "Args must not be null");
//調用Source對應用參數進行解析
this.source = new Source(args);
this.args = args;
}
Source(String[] args) {
//調用父類的構造函數 Source的繼承關係如下圖所示
super(args);
}
public SimpleCommandLinePropertySource(String... args) {
//對應參數進行解析的工作
super(new SimpleCommandLineArgsParser().parse(args));
}
大家在配置應用參數的時候,是這樣這樣配置的 - -key=value,爲什麼要以- -開頭呢?在SimpleCommandLineArgsParser的parse方法中你會找到答案。並且Source這個類還繼承類PropertySource這個類。
//準備環境變量
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//獲取環境變量
ConfigurableEnvironment environment = getOrCreateEnvironment();
//將應用參數放入到環境變量持有對象中
configureEnvironment(environment, applicationArguments.getSourceArgs());
//監聽器監聽環境變量對象的變化
listeners.environmentPrepared(environment);
//如果非web環境,則轉換爲StandardEnvironment對象
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
//如果已經創建過存放環境變量的對象了,則直接返回
if (this.environment != null) {
return this.environment;
}
//如果是web環境則創建StandardServletEnvironment對象
if (this.webEnvironment) {
return new StandardServletEnvironment();
}
//非web環境,創建StandardEnvironment
return new StandardEnvironment();
}
StandardServletEnvironment的UML圖如下所示,StandardServletEnvironment集成了系統變量、環境變量、配置屬性信息等內容。這些內容我們以後單開一個篇章來說一下。
//這句話是輸出SpringBoot的Banner信息,可以從指定的位置加載信息,可以輸出爲文字形式,也可以輸出爲圖片形式,如我們常見的SpringBoot的logo就是在這裏輸出的
Banner printedBanner = printBanner(environment);
Banner的UML類圖如下所示:
我們最常見的SpringBoot的logo“圖像”就在SpringBootBanner這個類中定義的,這個也是SpringBoot默認的Banner類。
//創建SpringBoot的應用上下文
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
//DEFAULT_WEB_CONTEXT_CLASS = org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
/// DEFAULT_CONTEXT_CLASS = org.springframework.context.annotation.AnnotationConfigApplicationContext
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
因爲我們是web開發環境,所以這裏我們的web上下文是AnnotationConfigEmbeddedWebApplicationContext這個對象,一定要記住這個類,要仔細的看看它的UML類圖。