Spring-boot---SpringApplication準(zhǔn)備階段的加載(二)

學(xué)習(xí)筆記是學(xué)習(xí)了 小馬哥在慕課網(wǎng)的 《Spring Boot 2.0深度實(shí)踐之核心技術(shù)篇》根據(jù)自己的需要和理解做的筆記皱坛。

應(yīng)用上下文初始器(ApplicationContextInitializer)

利用Spring工廠機(jī)制,實(shí)現(xiàn)類org.springframework.core.io.support.SpringFactoriesLoader

實(shí)例化 ApplicationContextInitializer 實(shí)現(xiàn)類着裹,并排序?qū)ο蠹稀?/p>

自己追了一下源碼,發(fā)現(xiàn)Spring-boot加載應(yīng)用文上下初始器的順序是

org.springframework.boot.SpringApplication#SpringApplication()->org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames->org.springframework.boot.SpringApplicatior#run()

具體代碼實(shí)現(xiàn)org.springframework.boot.SpringApplication#SpringApplication()

1.spring-boot使用setInitializers方法加載所有實(shí)現(xiàn)ApplicationContextInitializer.class的類,即全部上下文實(shí)現(xiàn)類助被。

/**
 * Create a new {@link SpringApplication} instance. The application context will load
 * beans from the specified primary sources (see {@link SpringApplication class-level}
 * documentation for details. The instance can be customized before calling
 * {@link #run(String...)}.
 * @param resourceLoader the resource loader to use
 * @param primarySources the primary bean sources
 * @see #run(Class, String[])
 * @see #setSources(Set)
 */
@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 = deduceWebApplicationType();
   //加載上下文初始器 ApplicationContextInitializer.class 的實(shí)現(xiàn)類
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   //加載監(jiān)聽初始器 ApplicationListener.class 的實(shí)現(xiàn)類
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

2.我們來看一下加載上下文實(shí)體的方法

org.springframework.boot.SpringApplication#getSpringFactoriesInstances()

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
   //在這里調(diào)用了 SpringFactoriesLoader 中的loadFactoryNames方法蠢涝,
    //來插入想要實(shí)現(xiàn)的上下文
   Set<String> names = new LinkedHashSet<>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

在這里注意一點(diǎn)就是spring-boot 使用了 Thread.currentThread().getContextClassLoader()來獲取了當(dāng)前類加載器鲸阻,這里就要介紹一下SPI機(jī)制(我也是在看這段代碼中,無法理解這段跋涣,在網(wǎng)上查了一些資料,簡(jiǎn)單的理解了一下這里為什么用這個(gè)方法鸟悴。)

SPI機(jī)制(Service Provider Interface)其實(shí)源自服務(wù)提供者框架(Service Provider Framework陈辱,參考【EffectiveJava】page6),是一種將服務(wù)接口與服務(wù)實(shí)現(xiàn)分離以達(dá)到解耦细诸、大大提升了程序可擴(kuò)展性的機(jī)制沛贪。引入服務(wù)提供者就是引入了spi接口的實(shí)現(xiàn)者,通過本地的注冊(cè)發(fā)現(xiàn)獲取到具體的實(shí)現(xiàn)類震贵,輕松可插拔利赋。

這里的ClassLoader是spring-boot的類加載器,在這突然又想到了JVM的雙親委派模型,感興趣的同學(xué)可以了解一下猩系。那么這個(gè)類加載器的SPI機(jī)制實(shí)現(xiàn)類都有哪些媚送? 就是配置資源META-INF/spring.factories 中的內(nèi)容。

3.我們來看一下引用SpringFactoriesLoader#SpringFactoriesLoader的方法是做什么的寇甸。

/**
 * Load the fully qualified class names of factory implementations of the
 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
 * class loader.
 * @param factoryClass the interface or abstract class representing the factory
 * @param classLoader the ClassLoader to use for loading resources; can be
 * {@code null} to use the default
 * @see #loadFactories
 * @throws IllegalArgumentException if an error occurs while loading factory names
 */
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();
   return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

在源碼注釋中可以清晰的看到該方法的作用塘偎,首先要了解到FACTORIES_RESOURCE_LOCATION全局變量的定義

/**
 * The location to look for factories.
 * <p>Can be present in multiple JAR files.
 */
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

META-INF/spring.factories存放的就是所有spring-boot實(shí)現(xiàn)的各種繼承規(guī)約接口的實(shí)現(xiàn)類的類路徑。

了解了這個(gè)全局變量拿霉,那么這段注釋我們就可以明白了吟秩。

/**Load the fully qualified class names of factory implementations of the given type from {@value #FACTORIES_RESOURCE_LOCATION}*/FACTORIES_RESOURCE_LOCATION中獲取所有繼承已知工廠類別的類全限定名。

4.既然都到這了绽淘,我們就不能不看一下SpringFactoriesLoader#loadSpringFactories(classLoader)這個(gè)方法涵防。

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   MultiValueMap<String, String> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   try {
       /**
       * 獲取所有實(shí)現(xiàn)工廠接口的類的全限定名
       */
      Enumeration<URL> urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            List<String> factoryClassNames = Arrays.asList(
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
            result.addAll((String) entry.getKey(), factoryClassNames);
         }
      }
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

通過 classLoader.getResourcesClassLoader.getSystemResources來獲取META-INF/spring.factories下的內(nèi)容。比如:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

然后通過解析Properties內(nèi)容來獲取全限定名收恢。

最后將想獲取的全限定名返回給

org.springframework.boot.SpringApplication#getSpringFactoriesInstances()方法:

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
   Set<String> names = new LinkedHashSet<>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //使用反射機(jī)制生成實(shí)例對(duì)象
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
    //進(jìn)行實(shí)例集合排序
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

通過#createSpringFactoriesInstances 方法生成實(shí)例以及調(diào)用AnnotationAwareOrderComparator.sort()進(jìn)行排序武学,而排序需要實(shí)現(xiàn)org.springframework.core.Ordered接口或者標(biāo)注org.springframework.core.Ordered注解,排序不是強(qiáng)制性的伦意。就像META-INF/spring.factories中應(yīng)用上下文初始器的實(shí)現(xiàn)類 org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer

org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener都沒有實(shí)現(xiàn)排序的接口或者注解火窒。

說了這么多,接下來讓我們自己實(shí)現(xiàn)一個(gè)上下文初始器吧驮肉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末熏矿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子离钝,更是在濱河造成了極大的恐慌票编,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卵渴,死亡現(xiàn)場(chǎng)離奇詭異慧域,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)浪读,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門昔榴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碘橘,你說我怎么就攤上這事互订。” “怎么了痘拆?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵仰禽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我纺蛆,道長(zhǎng)吐葵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任桥氏,我火速辦了婚禮折联,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘识颊。我一直安慰自己诚镰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布祥款。 她就那樣靜靜地躺著清笨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刃跛。 梳的紋絲不亂的頭發(fā)上抠艾,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音桨昙,去河邊找鬼检号。 笑死腌歉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的齐苛。 我是一名探鬼主播翘盖,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼凹蜂!你這毒婦竟也來了馍驯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤玛痊,失蹤者是張志新(化名)和其女友劉穎汰瘫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體擂煞,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡混弥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了对省。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剑逃。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖官辽,靈堂內(nèi)的尸體忽然破棺而出蛹磺,到底是詐尸還是另有隱情,我是刑警寧澤同仆,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布萤捆,位于F島的核電站,受9級(jí)特大地震影響俗批,放射性物質(zhì)發(fā)生泄漏俗或。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一岁忘、第九天 我趴在偏房一處隱蔽的房頂上張望辛慰。 院中可真熱鬧,春花似錦干像、人聲如沸帅腌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽速客。三九已至,卻和暖如春五鲫,著一層夾襖步出監(jiān)牢的瞬間溺职,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浪耘,地道東北人乱灵。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像七冲,于是被迫代替她去往敵國(guó)和親痛倚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理癞埠,服務(wù)發(fā)現(xiàn)状原,斷路器聋呢,智...
    卡卡羅2017閱讀 134,717評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,867評(píng)論 6 342
  • 入門 介紹 Spring Boot Spring Boot 使您可以輕松地創(chuàng)建獨(dú)立的苗踪、生產(chǎn)級(jí)的基于 Spring ...
    Hsinwong閱讀 16,903評(píng)論 2 89
  • 夏日的午后,昏昏沉沉讓人難受削锰。今夏更甚通铲,大半個(gè)七月皆未下滴雨。毒辣的陽光器贩,實(shí)在不敢越門檐半步颅夺。 近日夜眠不佳,人也...
    小雅愛書畫閱讀 553評(píng)論 0 3
  • 守一老師早課收獲: 1蛹稍、自尊自愛吧黄,用人格保證,建立學(xué)習(xí)型家庭唆姐,師緣大家庭拗慨。 2、聽話照做奉芦,堅(jiān)持早起學(xué)習(xí)赵抢。做好榜樣,...
    譯文_閱讀 146評(píng)論 0 0