1. 程式人生 > 程式設計 >SpringBoot啟動過程的實現

SpringBoot啟動過程的實現

SpringBoot啟動過程分析,首先開啟SpringBoot的啟用入口Main類:

@SpringBootApplication
public class ApplicationMain{
 public static void main(String[] args) {
  SpringApplication.run(ApplicationMain.class,args);
 }
}

可以看到main方法裡面只有一行核心啟用類:SpringApplication.run(ApplicationMain.class,args);這個是關鍵,在改行打上斷點,debug模式啟動該main類。點選下一步進入SpringApplication的原始碼對應的run方法:

public static ConfigurableApplicationContext run(Object[] sources,String[] args) {
  return (new SpringApplication(sources)).run(args);
 }

初始化SpringApplication

SpringApplication例項化之前會呼叫構造方法進行初始化:

public SpringApplication(Object... sources) {
  this.bannerMode = Mode.CONSOLE;
  this.logStartupInfo = true;
  this.addCommandLineProperties = true;
  this.headless = true;
  this.registerShutdownHook = true;
  this.additionalProfiles = new HashSet();
  this.initialize(sources);
 }

而SpringApplication構造方法的核心是:this.initialize(sources);初始化方法,SpringApplication通過呼叫該方法來初始化。

private void initialize(Object[] sources) {
 if (sources != null && sources.length > 0) {
  this.sources.addAll(Arrays.asList(sources));
 }
 this.webEnvironment = deduceWebEnvironment();
 setInitializers((Collection) getSpringFactoriesInstances(
   ApplicationContextInitializer.class));
 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
 this.mainApplicationClass = deduceMainApplicationClass();
}

1.deduceWebEnvironment方法是用來判斷當前應用的環境,該方法通過獲取這兩個類來判斷當前環境是否是web環境,如果能獲得這兩個類說明是web環境,否則不是。

javax.servlet.Servlet
org.springframework.web.context.ConfigurableWebApplicationContext

2.getSpringFactoriesInstances方法主要用來從spring.factories檔案中找出key為ApplicationContextInitializer的類並例項化,然後呼叫setInitializers方法設定到SpringApplication的initializers屬性中。這個過程就是找出所有的應用程式初始化器。

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes,Object... args) {
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type,classLoader));
  List<T> instances = this.createSpringFactoriesInstances(type,parameterTypes,classLoader,args,names);
  AnnotationAwareOrderComparator.sort(instances);
  return instances;
 }
public static List<String> loadFactoryNames(Class<?> factoryClass,ClassLoader classLoader) {
  String factoryClassName = factoryClass.getName();

  try {
   //從spring.factories檔案中找出key為ApplicationContextInitializer的類
   Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
   ArrayList result = new ArrayList();

   while(urls.hasMoreElements()) {
    URL url = (URL)urls.nextElement();
    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
    String factoryClassNames = properties.getProperty(factoryClassName);
    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
   }

   return result;
  } catch (IOException var8) {
   throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]",var8);
  }
 }

當前的初始化器有如下幾個:

在這裡插入圖片描述

3.同理呼叫getSpringFactoriesInstances從spring.factories檔案中找出key為ApplicationListener的類並例項化,然後呼叫setListeners方法設定到SpringApplication的listeners屬性中。這個過程就是找出所有的應用程式事件監聽器。
當前的事件監聽器有如下幾個:

在這裡插入圖片描述

4.呼叫deduceMainApplicationClass方法找出main類,就是這裡的ApplicationMain類。

執行SpringApplication

初始化SpringApplication完成之後,呼叫run方法執行:

public ConfigurableApplicationContext run(String... args) {
  //計時器,統計任務的執行時間
  StopWatch stopWatch = new StopWatch();
  //開始執行
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  FailureAnalyzers analyzers = null;
  this.configureHeadlessProperty();
  // 獲取SpringApplicationRunListeners啟動事件監聽器,這裡只有一個EventPublishingRunListener
  SpringApplicationRunListeners listeners = this.getRunListeners(args);
  // 封裝成SpringApplicationEvent事件然後廣播出去給SpringApplication中的listeners所監聽
  listeners.starting();

  try {
   // 構造一個應用程式引數持有類
   ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
   // 準備並配置環境
   ConfigurableEnvironment environment = this.prepareEnvironment(listeners,applicationArguments);
    // 列印banner圖形
   Banner printedBanner = this.printBanner(environment);
   // 建立Spring容器
   context = this.createApplicationContext();
   new FailureAnalyzers(context);
   // 配置Spring容器
   this.prepareContext(context,environment,listeners,applicationArguments,printedBanner);
   // 容器上下文重新整理
   this.refreshContext(context);
   // 容器建立完成之後呼叫afterRefresh方法
   this.afterRefresh(context,applicationArguments);
   // 呼叫監聽器,廣播Spring啟動結束的事件
   listeners.finished(context,(Throwable)null);
   // 停止計時器
   stopWatch.stop();
   if (this.logStartupInfo) {
    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(),stopWatch);
   }

   return context;
  } catch (Throwable var9) {
   this.handleRunFailure(context,(FailureAnalyzers)analyzers,var9);
   throw new IllegalStateException(var9);
  }
 }

SpringApplicationRunListeners

1.獲取啟動事件監聽器,可以看看該方法:

SpringApplicationRunListeners listeners = 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));
 }

同樣的通過呼叫getSpringFactoriesInstances方法去META-INF/spring.factories檔案中拿到SpringApplicationRunListener監聽器,當前的SpringApplicationRunListener事件監聽器只有一個EventPublishingRunListener廣播事件監聽器:

在這裡插入圖片描述

SpringApplicationRunListeners內部持有SpringApplicationRunListener集合和1個Log日誌類。用於SpringApplicationRunListener監聽器的批量執行。

SpringApplicationRunListener用於監聽SpringApplication的run方法的執行,它定義了5個步驟:

1.starting:run方法執行的時候立馬執行,對應的事件型別是ApplicationStartedEvent
2.environmentPrepared:ApplicationContext建立之前並且環境資訊準備好的時候呼叫,對應的事件型別是ApplicationEnvironmentPreparedEvent
3.contextPrepared:ApplicationContext建立好並且在source載入之前呼叫一次,沒有具體的對應事件
4.contextLoaded:ApplicationContext建立並載入之後並在refresh之前呼叫,對應的事件型別是ApplicationPreparedEvent
5.finished:run方法結束之前呼叫,對應事件的型別是ApplicationReadyEvent或ApplicationFailedEvent

SpringApplicationRunListener目前只有一個實現類EventPublishingRunListener,詳見獲取SpringApplicationRunListeners。它把監聽的過程封裝成了SpringApplicationEvent事件並讓內部屬性ApplicationEventMulticaster介面的實現類SimpleApplicationEventMulticaster廣播出去,廣播出去的事件物件會被SpringApplication中的listeners屬性進行處理。

所以說SpringApplicationRunListener和ApplicationListener之間的關係是通過ApplicationEventMulticaster廣播出去的SpringApplicationEvent所聯絡起來的

2.啟動事件監聽器

通過listeners.starting()可以啟動事件監聽器SpringApplicationRunListener ,SpringApplicationRunListener 是一個啟動事件監聽器介面:

public interface SpringApplicationRunListener {
 void starting();

 void environmentPrepared(ConfigurableEnvironment var1);

 void contextPrepared(ConfigurableApplicationContext var1);

 void contextLoaded(ConfigurableApplicationContext var1);

 void finished(ConfigurableApplicationContext var1,Throwable var2);
}

SpringApplicationRunListener 介面的具體實現就是EventPublishingRunListener類,我們主要來看一下它的startting方法,該方法會封裝成SpringApplicationEvent事件然後廣播出去給SpringApplication中的listeners所監聽。

public void starting() {
  this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application,this.args));
 }

配置並準備環境

private ConfigurableEnvironment prepareEnvironment(
  SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {
 // 建立應用程式的環境資訊。如果是web程式,建立StandardServletEnvironment;否則,建立StandardEnvironment
 ConfigurableEnvironment environment = getOrCreateEnvironment();
 // 配置環境資訊。比如profile,命令列引數
 configureEnvironment(environment,applicationArguments.getSourceArgs());
 // 廣播出ApplicationEnvironmentPreparedEvent事件給相應的監聽器執行
 listeners.environmentPrepared(environment);
 // 環境資訊的校對
 if (!this.webEnvironment) {
  environment = new EnvironmentConverter(getClassLoader())
    .convertToStandardEnvironmentIfNecessary(environment);
 }
 return environment;
}

判斷環境,如果是web程式,建立StandardServletEnvironment;否則,建立StandardEnvironment。

private ConfigurableEnvironment getOrCreateEnvironment() {
  if (this.environment != null) {
   return this.environment;
  } else {
   return (ConfigurableEnvironment)(this.webEnvironment ? new StandardServletEnvironment() : new StandardEnvironment());
  }
 }

建立Spring容器上下文

protected ConfigurableApplicationContext createApplicationContext() {
 Class<?> contextClass = this.applicationContextClass;
 if (contextClass == null) {
  try {
   // 判斷是否是web應用,
   // 如果是則建立AnnotationConfigEmbeddedWebApplicationContext,否則建立AnnotationConfigApplicationContext
   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);
}

配置Spring容器上下文

private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment,SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments,Banner printedBanner) {
 // 設定Spring容器上下文的環境資訊
 context.setEnvironment(environment);
 // Spring容器建立之後做一些額外的事
 postProcessApplicationContext(context);
 // SpringApplication的初始化器開始工作
 applyInitializers(context);
 // 遍歷呼叫SpringApplicationRunListener的contextPrepared方法。目前只是將這個事件廣播器註冊到Spring容器中
 listeners.contextPrepared(context);
 if (this.logStartupInfo) {
  logStartupInfo(context.getParent() == null);
  logStartupProfileInfo(context);
 }

 // 把應用程式引數持有類註冊到Spring容器中,並且是一個單例
 context.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);
 if (printedBanner != null) {
  context.getBeanFactory().registerSingleton("springBootBanner",printedBanner);
 }

 // 載入sources,sources是main方法所在的類
 Set<Object> sources = getSources();
 Assert.notEmpty(sources,"Sources must not be empty");
 // 將sources載入到應用上下文中。最終呼叫的是AnnotatedBeanDefinitionReader.registerBean方法
 load(context,sources.toArray(new Object[sources.size()]));
 // 廣播出ApplicationPreparedEvent事件給相應的監聽器執行
 // 執行EventPublishingRunListener.contextLoaded方法
 listeners.contextLoaded(context);
}

Spring容器建立之後回撥方法postProcessApplicationContext

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
 // 如果SpringApplication設定了例項命名生成器,則註冊到Spring容器中
 if (this.beanNameGenerator != null) {
  context.getBeanFactory().registerSingleton(
    AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);
 }
 // 如果SpringApplication設定了資源載入器,設定到Spring容器中
 if (this.resourceLoader != null) {
  if (context instanceof GenericApplicationContext) {
   ((GenericApplicationContext) context)
     .setResourceLoader(this.resourceLoader);
  }
  if (context instanceof DefaultResourceLoader) {
   ((DefaultResourceLoader) context)
     .setClassLoader(this.resourceLoader.getClassLoader());
  }
 }
}

初始化器開始工作

protected void applyInitializers(ConfigurableApplicationContext context) {
 // 遍歷每個初始化器,呼叫對應的initialize方法
 for (ApplicationContextInitializer initializer : getInitializers()) {
  Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
    initializer.getClass(),ApplicationContextInitializer.class);
  Assert.isInstanceOf(requiredType,context,"Unable to call initializer.");
  initializer.initialize(context);
 }
}

Spring容器建立完成之後會呼叫afterRefresh方法

ApplicationRunner、CommandLineRunner類都是在在afterRefresh方法中呼叫的,也就是說在Spring容器建立之後執行的。

protected void applyInitializers(ConfigurableApplicationContext context) {
 // 遍歷每個初始化器,呼叫對應的initialize方法
 for (ApplicationContextInitializer initializer : getInitializers()) {
  Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
    initializer.getClass(),"Unable to call initializer.");
  initializer.initialize(context);
 }
}

參考:https://blog.wangqi.love/articles/Spring/SpringBoot啟動過程.html

到此這篇關於SpringBoot啟動過程的實現的文章就介紹到這了,更多相關SpringBoot啟動過程內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!