天天看點

springboot啟動流程源碼解析(帶流程圖)

作者:wuweixianzheng

大緻流程如下:

1. 初始化SpringApplication,從META-INF下的spring.factories讀取ApplicationListener/ApplicationContextInitializer

2.運作SpringApplication的run方法

3.讀取項目中環境變量、jvm配置資訊、配置檔案資訊等

4.建立Spring容器對象(ApplicationContext)

5. 利用ApplicationContextInitializer初始化Spring容器對象,讀取啟動類

6.調用spring的refresh加載IOC容器、自動配置類,并建立bean、servlet容器等資訊

7.springboot會調用很多監聽器

8.如果啟動時發生異常,則發送ApplicationFailedEvent事件

下面對上述流程進行源碼細化分析,首先調用SpringApplication.run啟動springboot應用:

@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
@EnableTransactionManagement(proxyTargetClass = true)
@Import(MyDynamicDataSourceConfig.class)
public class MySpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApplication.class, args);
    }
}           

進入run方法後,會進行SpringApplication進行啟動,分兩大步,第一步初始化SpringApplication,第二步調用run方法:

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

第一步初始化SpringApplication,new SpringApplication(primarySources)的流程如下(具體方法含義參考注釋):

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  // 根據classpath下的類,推算目前web應用類型(REACTIVE/SERVLET/NONE)
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
  // 擷取BootstrapRegistryInitializer對象,從META-INF/spring.factories中讀取key為BootstrapRegistryInitializer,并執行個體化出對象
 // BootstrapRegistryInitializer的作用是可以初始化BootstrapRegistry
  this.bootstrapRegistryInitializers = new ArrayList<>(
         getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
  //去spring.factories中去擷取所有key:org.springframework.context.ApplicationContextInitializer為了初始化Spring容器ApplicationContext對象(可以利用
 //ApplicationContextInitializer向Spring容器中添加ApplicationListener)
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  //去spring.factories中去擷取所有key: org.springframework.context.ApplicationListener,ApplicationListener是Spring中的監聽器
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  //推測main()方法所在的類
   this.mainApplicationClass = deduceMainApplicationClass();
}           

第二步調用run方法,初始化完SpringApplication開始運作run方法,源碼如下:

public ConfigurableApplicationContext run(String... args) {
   long startTime = System.nanoTime();//記錄時間
   //建立DefaultBootstrapContext對象,利用BootstrapRegistryInitializer初始化DefaultBootstrapContext對象
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
  // 開啟了Headless模式:
   configureHeadlessProperty();
  //擷取SpringApplicationRunListeners, //SpringBoot提供了一個EventPublishingRunListener,它實作了SpringApplicationRunListener接口
  //spring會利用這個類,釋出一個ApplicationContextInitializedEvent事件,可以通過定義ApplicationListener來消費這個事件
   SpringApplicationRunListeners listeners = getRunListeners(args);
  // 釋出ApplicationStartingEvent事件,在運作開始時發送
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
     // 根據指令行參數 執行個體化一個ApplicationArguments
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     // 預初始化環境(見下面的源碼分析): 讀取環境變量(作業系統的環境變量/JVM的環境變量),讀取配置檔案資訊(基于監聽器,會利用EventPublishingRunListener釋出一個ApplicationEnvironmentPreparedEvent事件,
		//預設會有一個EnvironmentPostProcessorApplicationListener來處理這個事件,當然也可以通過自定義ApplicationListener來處理這個事件,當ApplicationListener接收到這個事件之後,就會解析application.properties、application.yml檔案,
   //并添加到Environment中)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);// 列印Banner
     //據webApplicationType建立不同的Spring上下文容器(有三種)
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
     //預初始化spring上下文,見下面的源碼分析
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
     //重新整理Spring容器,這一步中建立并初始化bean,建立并啟動tomacat等(以tomcat為例,調到ServletWebServerApplicationContext的createWebServer()方法
     //最後執行TomcatServletWebServerFactory的getWebServer方法)
     refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
     //釋出ApplicationStartedEvent事件和AvailabilityChangeEvent事件
      listeners.started(context, timeTakenToStartup);
     // 擷取Spring容器中的ApplicationRunner/CommandLineRunner類型的Bean,并執行run方法
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);//釋出ApplicationFailedEvent事件
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
     //釋出ApplicationReadyEvent事件和AvailabilityChangeEvent事件
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);//釋出ApplicationFailedEvent事件
      throw new IllegalStateException(ex);
   }
   return context;
}           

預初始化環境,建立Environment對象源碼解析:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   // 根據webApplicationType 建立Environment 建立就會讀取: java環境變量和系統環境變量
   ConfigurableEnvironment environment = getOrCreateEnvironment();
  // 将指令行參數讀取環境變量中
   configureEnvironment(environment, applicationArguments.getSourceArgs());
  // 将@PropertieSource的配置資訊 放在第一位,它的優先級是最低的
   ConfigurationPropertySources.attach(environment);
  // 釋出了ApplicationEnvironmentPreparedEvent 的監聽器 讀取了全局配置檔案
   listeners.environmentPrepared(bootstrapContext, environment);
  // 将所有spring.main 開頭的配置資訊綁定到SpringApplication中
   DefaultPropertiesPropertySource.moveToEnd(environment);
   Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
      environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
   }
   ConfigurationPropertySources.attach(environment);//更新PropertySources
   return environment;
}           

預初始化spring上下文源碼解析:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   applyInitializers(context);// 拿到之前讀取到所有ApplicationContextInitializer的元件, 循環調用initialize方法
   listeners.contextPrepared(context);// 釋出了ApplicationContextInitializedEvent
   bootstrapContext.close(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
  // 擷取目前spring上下文beanFactory (負責建立bean)
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
      ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
      if (beanFactory instanceof DefaultListableBeanFactory) {
        //在SpringBoot 在這裡設定了不允許覆寫, 當出現2個重名的bean 會抛出異常
         ((DefaultListableBeanFactory) beanFactory)
               .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
      }
   }
  // 設定目前spring容器是不是要将所有的bean設定為懶加載
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
  // 讀取主啟動類 (因為後續要根據配置類解析配置的所有bean),将啟動類作為配置類注冊到Spring容器中
   load(context, sources.toArray(new Object[0]));
   listeners.contextLoaded(context);//讀取完配置類後發送ApplicationPreparedEvent,預設利用EventPublishingRunListener釋出一個ApplicationPreparedEvent事件
}           
springboot啟動流程源碼解析(帶流程圖)

springboot啟動流程圖

繼續閱讀