Nacos源码分析五、BootstrapApplicationListener运行原理(2)

x33g5p2x  于2021-12-20 转载在 Bootstrap  
字(5.4k)|赞(0)|评价(0)|浏览(517)

接着上篇,我们看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上下文的自动装配。

相关文章