啟動原理
注意:使用版本為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(); } } }