天天看點

spring boot 啟動卡住_SpringBoot的啟動過程

SpringBoot的啟動過程

今天我們通過調試的方式講述一下SpringBoot的啟動的過程,加深自己的了解。

一、運作環境介紹

我們的運作環境是web的運作環境。

JDK:1.8

SpringBoot:2.2.2.RELEASE

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>

<groupId>com.breakpointgroupId>
<artifactId>spring-boot-learnartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.2.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>




<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>

<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>

<executable>trueexecutable>
configuration>
plugin>
plugins>
build>
project>
           

二、啟動的過程

2.1 建立SpringApplication對象并且執行run方法。

//org.springframework.context.ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
           

根據上面的執行的方式,可以總結為Spring啟動的時候,一共做了2件事情:

1)、建立SpringApplication對象

2)、執行這個對象的run方法。

那麼接下來分别看一看這2個方法都給咱們做了哪些的操作?

2.2 建立SpringApplication對象

建立SpringApplication對象是SpringBoot執行的第一個步驟:

// org.springframework.boot.SpringApplication#SpringApplication()
//
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 設定資源的加載對象
this.resourceLoader = resourceLoader;
//判斷primarySources是不是null
		Assert.notNull(primarySources, "PrimarySources must not be null");
// 設定主要資源的對象
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 決定目前應用的類别
// 由于我們用的是web,是以目前的應用視為 WebApplicationType.SERVLET 是一個服務
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 從項目的META-INF/spring.factories檔案裡找到所有的ApplicationContextInitializer的元件,設定到this.initializers對象上
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 這個和上面的相似,将META-INF/spring.factories所有的ApplicationListener對象擷取出來,設定到this.listeners這個對象上
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 決定哪一個類是祝啟動類
/*
          StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
          for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
              return Class.forName(stackTraceElement.getClassName());
            }
          }
 		上面的方式用了一個抛出異常的方式來決定哪一個類的裡面存在main方法。
 			*/
this.mainApplicationClass = deduceMainApplicationClass();
}
           

決定目前的應用是web還是

spring boot 啟動卡住_SpringBoot的啟動過程

2.3 執行run方法

下面的代碼就是執行所有的run的方法。

public ConfigurableApplicationContext run(String... args) {
// 建立一個螢幕,監視整個項目的啟動,執行的開始時間以及結束的時間
		StopWatch stopWatch = new StopWatch();
// 開始監視
		stopWatch.start();
// 生命ConfigurableApplicationContext對象,也就是IOC容器,後面有具體的建立的過程
		ConfigurableApplicationContext context = null;
// 建立 exceptionReporters 、也就是異常報告對象,如果我們的應用在啟動的過場中,出現了異常,這個時候,會報告相關的操作
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 設定 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS 的資訊
configureHeadlessProperty();
// 從 META-INF/spring.factories 擷取到所有的SpringApplicationRunListener元件
		SpringApplicationRunListeners listeners = getRunListeners(args);
// 這個listeners開始監聽
		listeners.starting();
try {
// 設定應用的啟動參數 也就是main防範的args參數
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 準備環境并且配置環境設定以及綁定到bindToSpringApplication上
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置環境
configureIgnoreBeanInfo(environment);
// 列印Banner 也就是springboot的圖示
			Banner printedBanner = printBanner(environment);
// 建立IOC容器對象,建立的過程中,根據不同的類别建立不同的IOC容器對象
/*
      	switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
      */
			context = createApplicationContext();
// 擷取到錯誤的報告
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//準備上下文環境
// 1/IOC容器設定環境
// 2.執行ApplicationContext 的後置處理器
// 3.調用ApplicationContextInitializer 的initialize方法在refreshed之前
// 4. 家在bean的定義到IOC容器
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 重新整理IOC容器
// 實際上就是調用Spring啟動的核心的過程,調用各種的後置處理器以及初始化我們的bean的元件,并且将他們加入到IOC的容器中。
// 這一步,也建立了enbedded的tomcat的對象,并且在我們的配置好的端口上啟動了
refreshContext(context);
// 準備好IOC的容器後的操作
afterRefresh(context, applicationArguments);
// 停止應用的監控,實際上,我們的主要的應用已經啟動完成啦
			stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// SpringApplicationRunListener 的元件開始監聽,也就是調用arted(context);的方法
			listeners.started(context);
// 調用所有的Runners
/*
      	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
				runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
				AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
          if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
          }
          if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
          }
        }
        根據上面的代碼,可以知道 調用了所有的ApplicationRunner 以及 CommandLineRunner的元件,也就是最後的一次回調了。
      */
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 如果有異常,那麼就會報告異常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
// 運作listeners.running
			listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
           

三、後記

經過上面的調試,更加的清晰的認識了SpringBoot的啟動過程,SpringBoot還有其他許多的東西,比如自動裝配等等這些比較重要的知識。

繼續閱讀