天天看點

springboot源碼學習(一)前言

Springboot源碼學習(一)

  • 前言
    • Springboot啟動入口
    • SpringApplication構造方法
    • getSpringFactoriesInstances方法跟蹤
    • 總結SpringApplication構造方法

前言

提示:本系列部落格為記錄部落客自己學習springboot源碼過程,如果不對之處,歡迎大家一起來讨論學習

Springboot啟動入口

public static void main(String[] args) {
       ApplicationContext context = SpringApplication.run(CloudTvApplication.class, args);
   }
           
  • 在我們主程式中,通過調用SpringApplication.run方法,啟動我們的springboot應用,

    此處我們跟進run方法進行檢視。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
   	return run(new Class<?>[] { primarySource }, args);
   }
           
  • 我們進入run方法,發現裡面是個重載的方法,繼續跟進
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   	return new SpringApplication(primarySources).run(args);
   }
           
  • 該方法中,程式做了兩件事:

      1、new一個SpringApplication對象

      2、執行SpringApplication的run方法

    下面我們分别就這兩個動作,分别進入對應的類中進行跟進,首先我們先看new SpringApplication()方法中,做了哪些事情,點進去。

SpringApplication構造方法

@SuppressWarnings({ "unchecked", "rawtypes" })
   public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   	   this.resourceLoader = resourceLoader;
   	   Assert.notNull(primarySources, "PrimarySources must not be null");
       this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   	   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   	   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   	   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   	   this.mainApplicationClass = deduceMainApplicationClass();
   }
           
  • 進入SpringApplication的構造方法,有兩個參數,第一個參數固定會傳null, 第二個參數primarySources也就是我們的啟動類CloudTvApplication.class,在主程式main方法傳入的參數。
  • 進入方法内部,首先進行resourceLoader 的指派,該處為null。
  • 然後進行斷言判斷我們的primarySources是否為空。
  • 将我們的primarySources轉換為list指派,目前list裡面隻有個元素,就是我們的啟動類。
  • WebApplicationType.deduceFromClasspath()這個方法,是用來推斷我們應用的類型,目前有三個值NONE,SERVLET,REACTIVE,怎麼推斷的邏輯,也比較簡單,是通過Class.forName看是否能夠加載對應的類來判斷啟動的類型。
    springboot源碼學習(一)前言

getSpringFactoriesInstances方法跟蹤

  • 下面兩個方法setInitializers和setListeners從代碼層面來說,他們邏輯相同,都是設定一些需要提前初始化好的類,不同的就是類的類型不同而已,我們看到,這兩個方法都同時調用了getSpringFactoriesInstances這個方法,那麼這個方法是做啥的,我們點進去看看。
    springboot源碼學習(一)前言
    我們分析下getSpringFactoriesInstances這個方法,第一步擷取classLoader,第二步調用SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,通過該方法名稱和傳回類型,我們猜測應該是去擷取一些類的名稱之類的,我們點進去跟進
    springboot源碼學習(一)前言
    該方法主要做了兩件事,第一擷取資源路徑,這邊的檔案路徑,就是如下的,我們在springboot的包中可以找到
    springboot源碼學習(一)前言
    第二就是去解析spring.factories檔案裡面的内容,裡面其實就是一個個key-value形式的配置,其中value可能會有多個值,是以這邊使用的是MultiValueMap來進行存儲,我們大概看下spring.factories都有哪些内容:
    springboot源碼學習(一)前言
    裡面一堆配置類,我們常見的一些Listenner也都會在裡面。
  • 最後我們的loadFactoryNames方法,其實就是根據對應的類名,傳回配置檔案對應的類集合,然後轉成set集合傳回
  • 拿到對應類型的類集合後,接下來調用createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)方法,該方法做的事情,就是根據上面擷取的類,然後分别通過反射方法,擷取對應的執行個體,這邊邏輯比較簡單
    springboot源碼學習(一)前言
  • 最後一個方法AnnotationAwareOrderComparator.sort(instances),就是對生成的執行個體進行一個排序,然後傳回排好序的集合。
  • 至此,我們就已經看完getSpringFactoriesInstances這個方法所做的事情,總結一下,該方法其實就是根據對應的Class類型,到META-INF/spring.factories這個檔案中,找到對應配置的類,然後進行執行個體化傳回。
  • 我們回到構造函數的最後一個方法,猜測該方法名稱,我們大緻可以推斷該方法主要是進行主函數的推斷,跟進方法
    springboot源碼學習(一)前言
    方法内部,我們看到,其實就是通過堆棧的資訊,比對出帶有main方法的名稱的類,然後進行加載,這邊就會将我們的啟動類CloudTvApplication指派到mainApplicationClass這個屬性值。
  • 到這裡,我們已經看完了SpringApplication的構造方法。

總結SpringApplication構造方法

  • 我們這邊總結下SpringApplication裡面幾個方法所做的事情
  1. 推斷WebApplicationType的值。
  2. 初始化ApplicationContextInitializer類型的執行個體。
  3. 初始化ApplicationListener類型的執行個體。
  4. 推斷我們的主程序main函數的類。
  • 這期的源碼跟蹤,我們就到構造方法結束,run方法的執行,我們在後面的部落格跟新繼續進行,歡迎大家一起來學習讨論。