目錄
本文是對Springboot1.5x源碼中的run方法和相關源碼進行分析。
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
public ConfigurableApplicationContext run(String... args) {
//1.設置時間監控
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//2.初始化應用上下文配置
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//3.java.awt.headless是J2SE的一種模式用於在缺少顯示屏、鍵盤或者鼠標時的系統配置,很多監控工具如jconsole 需要將該值設置爲true,系統變量默認爲true
configureHeadlessProperty();
//獲取spring.factories中的監聽器變量,args爲指定的參數數組,默認爲當前類SpringApplication
//4:獲取並啓動監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//5:構造容器環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//打印banner
Banner printedBanner = printBanner(environment);
//6:創建容器
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//7:準備應用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//8:刷新應用上下文
refreshContext(context);
//9:應用上下文刷新後置處理
afterRefresh(context, applicationArguments);
//10:發佈應用上下文啓動完成事件
listeners.finished(context, null);
//11.停止計時監控類
stopWatch.stop();
//12.輸出日誌記錄執行主類名、時間信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//13.返回上下文
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
1.創建並啓動計時監控類
StopWatch stopWatch = new StopWatch();
stopWatch.start();
關於StopWatch的相關源碼
public void start() throws IllegalStateException {
start("");
}
public void start(String taskName) throws IllegalStateException {
if (this.running) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.running = true;
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
首先記錄了當前任務的名稱,默認爲空字符串,然後記錄當前 Spring Boot 應用啓動的開始時間。
4.獲取並啓動監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
創建邏輯和之前實例化初始化器和監聽器的一樣,一樣調用的是 getSpringFactoriesInstances
方法來獲取配置的監聽器名稱並實例化所有的類。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
SpringApplicationRunListener
所有監聽器配置在 spring-boot-2.0.3.RELEASE.jar!/META-INF/spring.factories
這個配置文件裏面。
5、根據運行監聽器和應用參數來準備 Spring 環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
5.1關於prepartEnvironment的的源碼如下
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
// 獲取(或者創建)應用環境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置應用環境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 監聽裝配應用環境
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
5.2 配置應用環境
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
分兩種方式配置,一種是property sources;第二種是Profiles
6.創建容器
創建應用上下文
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
其實就是根據不同的應用類型初始化不同的上下文應用類。
7.準備應用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//綁定環境到上下文
context.setEnvironment(environment);
//配置上下文的 bean 生成器及資源加載器
postProcessApplicationContext(context);
//爲上下文應用所有初始化器
applyInitializers(context);
//觸發所有 SpringApplicationRunListener 監聽器的 contextPrepared 事件方法
listeners.contextPrepared(context);
//記錄啓動日誌
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 註冊兩個特殊的單例bean
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 加載所有資源
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
//觸發所有 SpringApplicationRunListener 監聽器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}
8.刷新應用上下文
refreshContext(context);
這個主要是刷新 Spring 的應用上下文,刷新容器
9.應用上下文刷新後置處理
下面是關於afterRefresh的相關源碼和方法
afterRefresh(context, applicationArguments);
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
ApplicationRunner、CommandLineRunner這兩個接口會在容器啓動後進行調用,相當於開機啓動,可以實現多個。
根據類型的到bean,根據order排序,循環調用接口的run()方法。
10.發佈應用上下文啓動完成事件
listeners.finished(context, null);
public void finished(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
//遍歷當前的監聽器,並觸發回調當前這個監聽器的方法
callFinishedListener(listener, context, exception);
}
}
11.停止計時監控
stopWatch.stop();
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
```java
計時監聽器停止,並統計一些任務執行信息。
**12 輸出日誌記錄執行主類名、時間信息**
```java
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
13.返回上下文
return context;
總結
springboot1.5x和springboot2.x的啓動過程大同小異,在創建容器的過程中沒有太大的區別,在run方法中,最大的不同就是在啓動容器的方式和流程。如有雷同,純屬正常。
Run方法中的區別
1.5x | 2.0x |
|
|