Spring boot 源碼分析(一)SpringApplication.run(上)

來源:互聯網
上載者:User

標籤:val   cto   代碼   main   抽象   sas   null   源碼   TE   

SpringApplication.run(Main.class, args);

從這個方法開始講吧:

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

ConfigurableApplicationContext 這個介面,熟悉spring源碼的童鞋們肯定一眼就會有親切感

至於不熟悉的童鞋們嘛...未來我可能會再開一個spring的坑

這裡不詳細介紹了

回到這個方法本身:

方法內容很簡單,指向了另一個run方法:

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

先建立了一個SpringApplication的對象,

然後再次指向了一個新的run方法。

不過在我們看下一個run方法之前,按照順序,

我們先來看一下SpringApplication的這個構造方法:

public SpringApplication(Class<?>... primarySources) {  this(null, primarySources);}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {  //將resourceLoader屬性設定為同名參數  this.resourceLoader = resourceLoader;  //判定primarySources是否為空白,若為空白則 throw new IllegalArgumentException("PrimarySources must not be null");  Assert.notNull(primarySources, "PrimarySources must not be null");  //確定primarySources不為空白的前提下,將primarySource注入到屬性primarySources中  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));  //設定屬性webApplicationType  //關於deduceWebApplicationType()這個方法,暫且按下不表,後面會講到  this.webApplicationType = deduceWebApplicationType();  //通過setter方法設定了initializers和listeners兩個屬性  //至於他到底設定了個什麼東西嘛,後面會講到  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));  //最後,設定屬性mainApplicationClass  //deduceMainApplicationClass()這個方法我們後面來講  this.mainApplicationClass = deduceMainApplicationClass();}

簡單總結一下就是:

  進行了一輪初始化;

  設定了一大堆不知道什麼鬼用的參數

對象產生了以後,我們來看看這個run方法...

等等

本著刨根問底的精神,我們先解決前面懸而未決的問題:

關於deduceWebApplicationType();這個方法:

private WebApplicationType deduceWebApplicationType() {  //這裡有三個參數  //REACTIVE_WEB_ENVIRONMENT_CLASS : dispatcherHandler的全類名  //MVC_WEB_ENVIRONMENT_CLASS : dispatcherServlet的全類名  //WEB_ENVIRONMENT_CLASSES : 是一個String[],裡面有兩項:  //一個是Servlet的全類名,一個是ConfigurableWebApplicationContext的全類名  //而isPresent方法,簡單講就是判斷這個類是否存在,存在則返回true,反之返回false  //所以說spring boot項目中maven的dependency不能隨便搞,不能多加也不能少加。  //spring boot會根據你的依賴來判斷你這個項目的性質  //後面還會有很多這種根據依賴的不同來判定的地方  //然後說一下這個WebApplicationType:  //是一個枚舉類,裡面有三項:  //SERVLET,REACTIVE,NONE  //只起一個標識作用,標明你這個項目到底是一個servlet項目,還是reactive項目,還是不連網(none)的項目    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)                && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {        return WebApplicationType.REACTIVE;    }    for (String className : WEB_ENVIRONMENT_CLASSES) {        if (!ClassUtils.isPresent(className, null)) {            return WebApplicationType.NONE;        }    }    return WebApplicationType.SERVLET;}

所以,前面構造器中:

  this.webApplicationType = deduceWebApplicationType();

就是根據你的jar包依賴,設定了這個項目的類型

然後是我們的兩個setter方法:

setInitializers 和 setListeners

你可能會很奇怪,為什麼都有構造器了,還要用setter方法。

答案很簡單啦,因為這個setter方法顯然不會是那種簡單的:

  this.xx = xx;

這點東西而已啦。

雖然,倒也沒複雜到哪去:

public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {    this.initializers = new ArrayList<>();    this.initializers.addAll(initializers);}

由以上代碼可知,這個initializers就是一個ArrayList,把你參數中的itializers裡面的所有項都放了進去。就醬;

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {    this.listeners = new ArrayList<>();    this.listeners.addAll(listeners);}

除了變數名和參數泛型之外,好像就跟上面那個一模一樣= =

兩個setter方法講完了,不過構造器中這兩個setter方法的參數,是由一個叫getSpringFactoriesInstances這個方法

下一步我們來看這個方法:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {    return getSpringFactoriesInstances(type, new Class<?>[] {});}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {  //擷取當前線程的類載入器    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();    // Use names and ensure unique to protect against duplicates(這個是官方注釋,意思是說用互不相同的名字來確保不發生重複)  //至於這個loadFactoryNames這個方法,簡單講就是根據介面或者抽象類別的class對象和類載入器,尋找所有對應的工廠類的全類名    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));  //顧名思義,createSpringFactoriesInstances就是建立Spring工廠的執行個體  //這個方法的具體代碼我們等下來看    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);  //簡單講,這就是排了個序。  //稍微詳細點講,根據對象的order value進行排序  //更詳細的講,等我開spring坑再說  //事先聲明,這坑我不一定會開= =  //這坑太大了  //寫spring是春天,講spring是冬天    AnnotationAwareOrderComparator.sort(instances);  //排完序了,返回回去。    return instances;}

所以上面的方法,實際重點就是那個createSpringFactoriesInstances。

再次說明一下上面那個方法中,我們調用createSpringFactoriesInstances方法時的五個參數:

type:一個Class對象。在前面的構造器中:

setInitializers方法裡注入的是ApplicationContextInitializer.class這個Class對象,

setListeners方法裡注入的是ApplicationListener.class這個Class對象。

paramerterTypes:一個Class數組。

在getSpringFactoriesInstances(Class<T> type)方法中,他注入的是一個空數組。

classLoader:類載入器。方法中用的是線程的類載入器。

args:一堆參數。

在getSpringFactoriesInstances(Class<T> type)方法中,他注入的是null。

names:一個LinkedHashSet<String>

由上面的type和classloader找到這個type所有對應的工廠類的全類名

好的請記住這些,來看下面的方法:

 

private <T> List<T> createSpringFactoriesInstances(      Class<T> type,      Class<?>[] parameterTypes,      ClassLoader classLoader,      Object[] args,      Set<String> names) {  //很顯然,instances就是我們最終要返回的list  //從這個arraylist的範圍來看,很顯然這個list裡的每一項都跟names有關  List<T> instances = new ArrayList<>(names.size());  for (String name : names) {    try {      //根據name和classloader來找類對象      //所以前面一定要保證這裡的Names不一樣不然list裡面就會有重複的了。      Class<?> instanceClass = ClassUtils.forName(name, classLoader);      //簡單講,看看type是不是instanceClass的超類或者介面。      //如果是,往下走;如果不是,報錯。      Assert.isAssignable(type, instanceClass);      //如果代碼執行到此,說明type是instanceClass的超類。      //parameterTypes由此可見是instanceClass的構造器的參數類型表      Constructor<?> constructor = instanceClass          .getDeclaredConstructor(parameterTypes);      //簡單講,就是用剛剛得到的構造器,和args來構造一個T的執行個體      //因此args和parameterTypes是一一對應的。      //前面已經判定過,type是instanceClass的超類或者介面。      //因此這個強制類型轉換是成立的。      T instance = (T) BeanUtils.instantiateClass(constructor, args);      //把這個執行個體加到最開始聲明的list裡面      instances.add(instance);    } catch (Throwable ex) {      //前面如果報錯了,那就再報個錯      //spring向來如此,報錯一個接著一個      //最終的效果就是,不管出了什麼p大點兒錯,都要先給你報滿滿一大長串的錯      //然後每次看console...哎,頭大      throw new IllegalArgumentException(          "Cannot instantiate " + type + " : " + name, ex);    }  }  //最終把最上面那個list返回回去  return instances;}

綜上所述,上面的方法就是建立了所有type所對應的工廠類的類對象。

此之謂createSpringFactoriesInstances

回到前面的構造方法:

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

就是將initializers初始化為ApplicationContextInitializer.class 所對應的所有工廠類的對象組成的集合

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

就是將listeners初始化為ApplicationListener.class所對應的所有工廠類的對象組成的集合

至此,構造方法完畢。

然後我們看run方法。。。

看個鬼啊,寫這麼多,你們看著累不?

我反正累了。

剩下的下次再說。

拜拜~~~~~~~~~

 

Spring boot 源碼分析(一)SpringApplication.run(上)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.