1. 程式人生 > 其它 >Nacos原始碼分析五、BootstrapApplicationListener執行原理(2)

Nacos原始碼分析五、BootstrapApplicationListener執行原理(2)

技術標籤:SpringCloudAlibabaNacosjavaspring cloud alibabanacos

接著上篇,我們看BootstrapImportSelectorConfiguration配置類:

@Configuration(proxyBeanMethods = false)
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {

}

Import了一個Selector:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
   // Use names and ensure unique to protect against duplicates
   List<String> names = new ArrayList<>(SpringFactoriesLoader
         .loadFactoryNames(BootstrapConfiguration.class, classLoader));
   names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(
         this.environment.getProperty("spring.cloud.bootstrap.sources", ""))));

   List<OrderedAnnotatedElement> elements = new ArrayList<>();
   for (String name : names) {
      try {
         elements.add(
               new OrderedAnnotatedElement(this.metadataReaderFactory, name));
      }
      catch (IOException e) {
         continue;
      }
   }
   AnnotationAwareOrderComparator.sort(elements);

   String[] classNames = elements.stream().map(e -> e.name).toArray(String[]::new);

   return classNames;
}

可以看到,類似SpringBoot的EnableAutoConfiguration,主要是載入自動配置的BootstrapConfiguration型別的配置類。

說明什麼呢?SpringCloud通過BootstrapConfiguration來進行新的上下文的配置自動裝配。

新的上下文執行run的時候,也會有環境準備過程,同樣也會觸發環境準備好的事件監聽,此時會呼叫到ConfigFileApplicationListener的監聽方法,此時因為設定了bootstrap屬性源,那麼就會去載入bootstrap的相關配置檔案,然後再去處理BootstrapImportSelectorConfiguration

的解析

然後我們看一下spring-cloud-alibaba-nacos-config裡的spring.factories:

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer

可以看到定義了NacosConfigBootstrapConfiguration類,作為BootstrapConfiguration自動配置。

在這裡插入圖片描述

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigBootstrapConfiguration {

   @Bean
   @ConditionalOnMissingBean
   public NacosConfigProperties nacosConfigProperties() {
      return new NacosConfigProperties();
   }

   @Bean
   @ConditionalOnMissingBean
   public NacosConfigManager nacosConfigManager(
         NacosConfigProperties nacosConfigProperties) {
      return new NacosConfigManager(nacosConfigProperties);
   }

   @Bean
   public NacosPropertySourceLocator nacosPropertySourceLocator(
         NacosConfigManager nacosConfigManager) {
      return new NacosPropertySourceLocator(nacosConfigManager);
   }

}

這裡定義了幾個例項:NacosConfigProperties、NacosConfigManager、NacosPropertySourceLocator,後面會去分析。

再回到BootstrapApplicationListener的bootstrapServiceContext方法,當新的上下文run完成後,會新增一個初始化器AncestorInitializer,裡面建立了一個ParentContextApplicationContextInitializer,通過這個建立新的上下文和原上下文的父子關係。這樣下次就可以直接取出來用,不用需要重新建立上下文了,然後刪除bootstrap屬性源。最後對老環境的屬性源進行整合:

mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
private void mergeDefaultProperties(MutablePropertySources environment,
                                    MutablePropertySources bootstrap) {
    String name = DEFAULT_PROPERTIES;
    if (bootstrap.contains(name)) {
        PropertySource<?> source = bootstrap.get(name);
        if (!environment.contains(name)) {
            environment.addLast(source);
        }
        else {
            PropertySource<?> target = environment.get(name);
            if (target instanceof MapPropertySource && target != source
                && source instanceof MapPropertySource) {
                Map<String, Object> targetMap = ((MapPropertySource) target)
                    .getSource();
                Map<String, Object> map = ((MapPropertySource) source).getSource();
                for (String key : map.keySet()) {
                    if (!target.containsProperty(key)) {
                        targetMap.put(key, map.get(key));
                    }
                }
            }
        }
    }
    mergeAdditionalPropertySources(environment, bootstrap);
}
private void mergeAdditionalPropertySources(MutablePropertySources environment,
			MutablePropertySources bootstrap) {
    PropertySource<?> defaultProperties = environment.get(DEFAULT_PROPERTIES);
    ExtendedDefaultPropertySource result = defaultProperties instanceof ExtendedDefaultPropertySource
        ? (ExtendedDefaultPropertySource) defaultProperties
        : new ExtendedDefaultPropertySource(DEFAULT_PROPERTIES,
                                            defaultProperties);
    for (PropertySource<?> source : bootstrap) {
        if (!environment.contains(source.getName())) {
            result.add(source);
        }
    }
    for (String name : result.getPropertySourceNames()) {
        bootstrap.remove(name);
    }
    addOrReplace(environment, result);
    addOrReplace(bootstrap, result);
}
private void addOrReplace(MutablePropertySources environment,
			PropertySource<?> result) {
    if (environment.contains(result.getName())) {
        environment.replace(result.getName(), result);
    }
    else {
        environment.addLast(result);
    }
}

之後再在原上下文中新增監聽器CloseContextOnFailureApplicationListener

event.getSpringApplication()
      .addListeners(new CloseContextOnFailureApplicationListener(context));

然後是apply方法:

private void apply(ConfigurableApplicationContext context,
      SpringApplication application, ConfigurableEnvironment environment) {
   if (application.getAllSources().contains(BootstrapMarkerConfiguration.class)) {
      return;
   }
   application.addPrimarySources(Arrays.asList(BootstrapMarkerConfiguration.class));
   @SuppressWarnings("rawtypes")
   Set target = new LinkedHashSet<>(application.getInitializers());
   target.addAll(
         getOrderedBeansOfType(context, ApplicationContextInitializer.class));
   application.setInitializers(target);
   addBootstrapDecryptInitializer(application);
}

在原上下文中新增一個標記配置類BootstrapMarkerConfiguration,標記bootstrap處理已經完成。然後再對初始化器排序。

總結

到這裡BootstrapApplicationListener的執行過程就分析完了。簡單來說就是建立了一個新的上下文並作為原上下文的父級,然後進行bootstrap配置檔案載入。

同時引入BootstrapConfiguration進行Bootstrap上下文的自動裝配。