SpringBoot啟動流程源碼分析

前言【SpringBoot啟動流程源碼分析】SpringBoot項目的啟動流程是很多面試官面試中高級Java程序員喜歡問的問題 。這個問題的答案涉及到了SpringBoot工程中的源碼 , 也許我們之前看過別的大牛寫過的有關SpringBoot項目啟動流程的文章,但是自己沒有去研究一遍總是會記憶不深刻 。有句話叫做“紙上來得終覺淺,絕知此事要躬行”,我覺得說得非常在理 。底層的東西 , 也只有自己深入研究過一遍甚至好幾遍源碼才能徹底搞懂并記憶牢固 。下面筆者來帶領大家詳細分析SpringBoot啟動過程中到底做了哪些事情 , 把本文仔細看完了,面對面試官問的有關SpringBoot啟動過程做了哪些工作的面試題就迎刃而解了!
啟動類入口方法首先我們通過SpringApplication類的靜態Run方法進入SpringBoot項目的啟動入口
/*** @param primarySource springboot啟動類* @param args 啟動參數*/public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class[]{primarySource}, args);}復制
從上面的源碼中我們可以看到SpringBoot啟動類返回的應用上下文類是ConfigurableApplicationContext
然后我們進入另一個靜態run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);}復制
在上面這個靜態run方法里面最終會通過SpringApplication類的構造函數實例化一個SpringApplication類實例對象,后面在調用SpringApplication實例對象的run方法
SpringApplication類實例化和初始化接下來我們看看SpringApplication類在實例化時做了什么事情
public SpringApplication(Class<?>... primarySources) {this((ResourceLoader)null, primarySources);}復制
可以看到在SpringApplication類上面這個構造方法里面又調用了另一個構造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {// 實例化sources屬性this.sources = new LinkedHashSet();// 打印模式為控制臺打印this.bannerMode = Mode.CONSOLE;// 設置記錄啟動日志信息標識為truethis.logStartupInfo = true;// 設置添加命令行屬性標識為truethis.addCommandLineProperties = true;//設置addConversionService屬性為truethis.addConversionService = true;// 設置headless屬性為truethis.headless = true;// 設置注冊應用關停鉤子屬性為truethis.registerShutdownHook = true;// 實例化additionalProfiles屬性this.additionalProfiles = new HashSet();//默認非自定義環境this.isCustomEnvironment = false;// 上一步傳過來的resourceLoader為nullthis.resourceLoader = resourceLoader;// 斷言primarySources參數不能為空,也就是springboot應用類不能為空Assert.notNull(primarySources, "PrimarySources must not be null");// 將傳遞過來的springboot啟動類參數轉成List后加入LinkedHashSet集合后賦值給primarySources屬性this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));// 根據類路徑推斷web應用類型this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));// 設置初始化器屬性// 設置監聽器屬性this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));// 推斷主啟動類this.mainApplicationClass = this.deduceMainApplicationClass();}復制
從上面的源碼中我們分析的結果來看,實例化SpringApplication類的過程做了以下幾件事情:

  • 初始化SpringApplication啟動類中的大部分屬性變量
  • 推斷web應用類型
  • 通過加載類路徑目錄META-INF下的spring.factories文件讀取出初始化器和監聽器集合并設置到SpringApplication實例對應的初始化器和監聽器屬性列表中
  • 推斷主啟動類并賦值給SpringApplication啟動類的mainApplicationClass屬性
推斷Web應用類型進入WebApplicationType#deduceFromClasspath方法
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {return REACTIVE;} else {String[] var0 = SERVLET_INDICATOR_CLASSES;int var1 = var0.length;for(int var2 = 0; var2 < var1; ++var2) {String className = var0[var2];if (!ClassUtils.isPresent(className, (ClassLoader)null)) {return NONE;}}return SERVLET;}}

推薦閱讀