切換語言為:簡體

SpringBoot啟動原理分析

  • 爱糖宝
  • 2024-07-22
  • 2071
  • 0
  • 0

啟動原理

注意:使用版本為spring-boot-2.2.2.RELEASE

springboot啟動的入口肯定是main方法啦,那就從main方法入口走起來看看是如何進行啟動的

@SpringBootApplication
public class ConsulApp {
    public static void main(String[] args) {
      	// 呼叫SpringApplication的靜態run方法
        SpringApplication.run(ConsulApp.class,args);
    }
}

進入main方法

// 這個primarySources是傳入進來的啟動類
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  // 先例項化SpringApplication
   return new SpringApplication(primarySources).run(args);
}

例項化SpringApplication

// this(null, primarySources)
// resourceLoader是null,primarySources是傳入進來的啟動類
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
  // 使用set進行去重
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  // 根據classpath中是否存在org.springframework.web.reactive.DispatcherHandler來判斷是否為REACTIVE
  // 根據classpath中是否存在"javax.servlet.Servlet"和"org.springframework.web.context.ConfigurableWebApplicationContext"來判斷是否為SERVLET
  // web應用的型別,是None表示非web專案  SERVLET表示普通的servlet web專案  REACTIVE表示響應式的web專案
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
  // 設定應用上下文初始化器  SpringFactoriesLoader從META-INF/spring.factories載入的,獲取 ApplicationContextInitializer 介面的所有配置的類路徑名稱,並進行例項化
 setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
  // 設定監聽器 SpringFactoriesLoader從META-INF/spring.factories載入的,獲取ApplicationListener介面的所有配置的類路徑名稱,並進行例項化
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  // 推斷主啟動類,透過構造一個執行時異常,再遍歷異常棧中的方法名,獲取方法名為 main 的棧幀,從來得到入口類的名字再返回該類
   this.mainApplicationClass = deduceMainApplicationClass();
}

執行SpringApplication例項的run方法

例項化SpringApplication之後,呼叫該物件的run方法

  • 進行計時,記錄整個過程的載入事件

  • 初始化應用上下文和異常報告集合,設定headless變數

  • 透過SpringFactoriesLoader載入SpringApplicationRunListener監聽器,呼叫starting方法,表示springboot要啟動了

  • 建立ConfigurableEnvironment,將配置的環境繫結到spring應用中(包括PropertySource和Profile),並呼叫SpringApplicationRunListener監聽器的environmentPrepared方法,應用的environment已經準備完畢

  • Banner列印並建立應用上下文

  • 建立應用上下文,根據webApplicationType決定建立不同的上下文

  • 準備應用上下文,執行初始化器ApplicationContextInitializer的initialize方法

  • 重新整理應用上下文

  • 計時停止,呼叫SpringApplicationRunListener監聽器的started方法,表示應用上下文已完成

  • 執行所有的Runner執行器(ApplicationRunner和CommandLineRunner)

  • 呼叫SpringApplicationRunListener監聽器的running方法,表示已經開始執行了

public ConfigurableApplicationContext run(String... args) {
  // NO1 
  // 建立計時監控物件,記錄整個過程的載入事件
   StopWatch stopWatch = new StopWatch();
  // 啟動計時監控,記錄開始時間
   stopWatch.start();
  // NO2
  // 初始化應用上下文和異常報告集合
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  
  // 設定系統屬性 java.awt.headless,預設true
   configureHeadlessProperty();
  // NO3
  // 建立SpringApplicationRunListeners監聽器,透過SpringFactoriesLoader載入,監聽器在spring.factories中SpringApplicationRunListener介面,預設是隻有org.springframework.boot.context.event.EventPublishingRunListener
  // 本質是一個事件釋出者
   SpringApplicationRunListeners listeners = getRunListeners(args);
  // 開始監聽,表示springboot要開始啟動了
  // 廣播ApplicationStartingEvent事件
   listeners.starting();
   try {
     // NO4
     // 初始化預設應用引數類
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     // 載入springboot配置環境
     // configurePropertySources(environment, args);  配置PropertySource
		// configureProfiles(environment, args);  配置profiles
     // 此時廣播了一個ApplicationEnvironmentPreparedEvent事件,通知事件監聽者,應用的environment已經準備完畢
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
     // NO5
     // Banner列印
      Banner printedBanner = printBanner(environment);
     // NO6 建立應用上下文,根據webApplicationType應用型別的不同,建立不同的上下文,透過Class.forName的方式
     // DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext"
     // DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"
     // DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"
      context = createApplicationContext();
     
     // 異常報告器,在spring.factories中SpringBootExceptionReporter介面
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
     // NO7
     // 準備應用上下文
     // 給ApplicationContext設定environment
		// 遍歷呼叫所有的ApplicationContextInitializer的 initialize()方法
    // 廣播ApplicationContextInitializedEvent事件,ApplicationContext初始化事件
    // 將所有的bean載入到容器中
    // 廣播ApplicationPreparedEvent事件,ApplicationContext準備事件
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
     
     // NO8
     // 重新整理應用上下文,獲取所有的BeanFactoryPostProcessor對容器進行一些額外操作
     // 其中對於@Configuration、@ComponentScan、@Import、@PropertySource、@ImportResource、@Bean註解都是在這裏處理的
     // 這裏的操作就是spring中的refresh方法那一套東西
      refreshContext(context);
     // 應用上下文重新整理後置處理(該方法為空方法)
      afterRefresh(context, applicationArguments);
     // 停止計時監控
      stopWatch.stop();
      if (this.logStartupInfo) {
        // 輸出主類名以及時間資訊
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
     // NO9
     // 廣播ApplicationStartedEvent事件,表示應用上下文已完成
      listeners.started(context);
     // NO10
     // 執行Runner執行器  ApplicationRunner和CommandLineRunner實現類
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
     // NO11
     // 釋出應用上下文就緒事件
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

NO7 準備應用上下文

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  // 設定環境
   context.setEnvironment(environment);
  // 配置上下文的bean生成器以及資源載入器
   postProcessApplicationContext(context);
  // 上下文初始化器執行initialize方法
   applyInitializers(context);
  // 觸發監聽器的contextPrepared事件
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
  // 註冊兩個特殊的單例bean
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
  // 載入所有資源
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
  // 載入bean
   load(context, sources.toArray(new Object[0]));
  // 觸發監聽器的contextLoaded事件
   listeners.contextLoaded(context);
}

NO8 重新整理應用上下文

這裏實際呼叫的就是spring中的refresh方法。

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
     // 設定beanFactory的一些屬性
     // 新增後置處理器
     // 設定忽略的自動裝配介面
     // 註冊一些元件
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.