@SpringBootApplication
@EnableFeignClients(defaultConfiguration = FeignClientsConfiguration.class)
public class DocApplication {
public static void main(String[] args) {
SpringApplication.run(DocApplication.class, args);
}
}
從run這個方法點擊 進入源碼可以發現最終調用的方法爲 SpringApplication.run(String...args)
public ConfigurableApplicationContext run(String... args) {//String... args 是可變參數 (jdk1.5之後的新特性) 類似 String[] args
// 1、創建並啓動計時監控類
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2、初始化應用上下文和異常報告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 3、設置系統屬性 `java.awt.headless` 的值,默認值爲:true
this.configureHeadlessProperty();
// 4、創建所有 Spring 運行監聽器併發布應用啓動事
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
// 5、初始化默認應用參數類
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 6、根據運行監聽器和應用參數來準備 Spring 環境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 7、創建 Banner 打印類
Banner printedBanner = this.printBanner(environment);
// 8、創建應用上下文
context = this.createApplicationContext();
// 9、準備異常報告器
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 10、準備應用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 11、刷新應用上下文
this.refreshContext(context);
// 12、應用上下文刷新後置處理
this.afterRefresh(context, applicationArguments);
// 13、停止計時監控類
stopWatch.stop();
// 14、輸出日誌記錄執行主類名、時間信息
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 15、發佈應用上下文啓動完成事件
listeners.started(context);
// 16、執行所有 Runner 運行器
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 17、發佈應用上下文就緒事件
listeners.running(context);
// 18、返回應用上下文
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
主要根據這個方法來分析啓動過程
1、創建並啓動計時器監控類StopWatch
StopWatch stopWatch = new StopWatch();
stopWatch.start();
StopWatch start部分的源碼
public void start() throws IllegalStateException {
this.start("");
}
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
this.currentTaskName = taskName;//記錄任務名稱
this.startTimeMillis = System.currentTimeMillis();//記錄任務開始時間
}
}
2、初始化應用上下文和異常報告集合
源碼如下 沒什麼講
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
3、設置系統屬性java.awt.headless的值
this.configureHeadlessProperty();
private void configureHeadlessProperty() {
System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));//headless默認值爲true
}
問題 Java.awt.headless = true 有什麼作用?
對於一個 Java 服務器來說經常要處理一些圖形元素,例如地圖的創建或者圖形和圖表等。這些API基本上總是需要運行一個X-server以便能使用AWT(Abstract Window Toolkit,抽象窗口工具集)。然而運行一個不必要的 X-server 並不是一種好的管理方式。有時你甚至不能運行 X-server,因此最好的方案是運行 headless 服務器,來進行簡單的圖像處理。
4、創建所有Spring運行監聽器併發布應用啓動事件
a、
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();//找到匹配的監聽器,廣播事件,開始監聽
a.1、首先是this.getRunListeners(args)的源碼
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
a.1.1、先看 this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//動態加載jar、資源文件 的線程上下文類加載器
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//方法獲取spring-boot-2.0.6.RELEASE.jar!/META-INF/spring.factories配置文件裏面所有的監聽器名稱
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);//實例化 上面得到的名稱集合
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
a.1.1.1、其中 SpringFactoriesLoader.loadFactoryNames(type, classLoader)) 中的部分源碼
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
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();
List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result.addAll((String)entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}
從中可以看出 loadSpringFactories方法直接加載所有的META-INF/spring.factories文件內容,其內部還是調用PropertiesLoaderUtils.loadProperties方法進行處理。 在此處使用了 緩存策略
a.1.1.2 this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); 的源碼
根據得到的名稱集合 實例化所有的監聽類
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;
}
5、初始化默認應用參數
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
6、根據運行監聽器和應用參數來準備 Spring 環境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
具體源碼
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//6.1 獲取(或者創建)應用環境
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 6.2 配置應用環境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
6.1獲取(或創建)應用環境
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
} else {
switch(this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
}
6.1 獲取(或者創建)應用環境
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
this.configurePropertySources(environment, args);
this.configureProfiles(environment, args);
}
這裏分爲以下兩步來配置應用環境。
- 配置 property sources
- 配置 Profiles
這裏主要處理所有 property sources 配置和 profiles 配置。
7、創建 Banner 打印類
Banner printedBanner = this.printBanner(environment);
用來打印 Banner 的處理類
8、創建應用上下文
context = this.createApplicationContext();
接下來 this.createApplicationContext()的源碼
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
根據不同的應用類型初始化不同的上下文應用類
9、準備異常報告器
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
邏輯和之前實例化初始化器和監聽器的一樣,一樣調用的是 getSpringFactoriesInstances
方法來獲取配置的異常類名稱並實例化所有的異常處理類。
該異常報告處理類配置在 spring-boot-2.0.6.RELEASE.jar!/META-INF/spring.factories
這個配置文件裏面。
10、準備應用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
該方法的源碼
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 10.1)綁定環境到上下文
context.setEnvironment(environment);
// 10.2)配置上下文的 bean 生成器及資源加載器
this.postProcessApplicationContext(context);
// 10.3)爲上下文應用所有初始化器
this.applyInitializers(context);
// 10.4)觸發所有 SpringApplicationRunListener 監聽器的 contextPrepared 事件方法
listeners.contextPrepared(context);
// 10.5)記錄啓動日誌
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
// 10.6)註冊兩個特殊的單例bean
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 10.7)加載所有資源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
// 10.8)觸發所有 SpringApplicationRunListener 監聽器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}
11、刷新應用上下文
this.refreshContext(context);
源碼如下
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
12、應用上下文刷新後置處理
this.afterRefresh(context, applicationArguments);
該源碼方法爲空
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
13、停止計時監控類
stopWatch.stop();
計時監聽停止,並統計一些任務執行信息
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
} else {
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new StopWatch.TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
}
14、輸出日誌記錄執行主類名、時間信息
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
15、發佈應用上下文啓動完成事件
listeners.started(context);
16、執行所有 Runner 運行器
this.callRunners(context, applicationArguments);
源碼爲
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);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}
執行所有 ApplicationRunner
和 CommandLineRunner
這兩種運行器
17、發佈應用上下文就緒事件並返回應用上下文
listeners.running(context);//觸發所有 SpringApplicationRunListener 監聽器的 running 事件方法。
return context;
總結
持續學習中