spring cloud config 配置加载源码解读

600 阅读1分钟

spring boot 整合spring cloud config通过ApplicationEnvironmentPreparedEvent事件触发,新建bootstrap 上下文加载ApplicationContextInitializer给SpringApplication ,从而进行加载配置中心配置。

加载路径源码

加载主要核心入口BootstrapApplicationListener

org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\

监听ApplicationEnvironmentPreparedEvent事件

public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {



public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
    ConfigurableEnvironment environment = event.getEnvironment();
    if ((Boolean)environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) {
        if (!environment.getPropertySources().contains("bootstrap")) {
            ConfigurableApplicationContext context = null;
            String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
            Iterator var5 = event.getSpringApplication().getInitializers().iterator();

            while(var5.hasNext()) {
                ApplicationContextInitializer<?> initializer = (ApplicationContextInitializer)var5.next();
                if (initializer instanceof ParentContextApplicationContextInitializer) {
                    context = this.findBootstrapContext((ParentContextApplicationContextInitializer)initializer, configName);
                }
            }

            if (context == null) {
                //构建bootstrap上下文容器加载 ApplicationContextInitializer
                context = this.bootstrapServiceContext(environment, event.getSpringApplication(), configName);
                event.getSpringApplication().addListeners(new ApplicationListener[]{new BootstrapApplicationListener.CloseContextOnFailureApplicationListener(context)});
            }
            this.apply(context, event.getSpringApplication(), environment);
        }
    }
}

//加载 ApplicationContextInitializer 到 SpringApplication
private void apply(ConfigurableApplicationContext context, SpringApplication application, ConfigurableEnvironment environment) {
    List<ApplicationContextInitializer> initializers = this.getOrderedBeansOfType(context, ApplicationContextInitializer.class);
    application.addInitializers((ApplicationContextInitializer[])initializers.toArray(new ApplicationContextInitializer[initializers.size()]));
    this.addBootstrapDecryptInitializer(application);
}

BootstrapImportSelectorConfiguration 为bootstrap容器入口配置类

private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment, final SpringApplication application, String configName) {
    ...
    builder.sources(new Class[]{BootstrapImportSelectorConfiguration.class});
    ConfigurableApplicationContext context = builder.run(new String[0]);
    context.setId("bootstrap");
    this.addAncestorInitializer(application, context);
    bootstrapProperties.remove("bootstrap");
    this.mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
    return context;
}

BootstrapImportSelectorConfiguration 导入BootstrapImportSelector类

@Import({BootstrapImportSelector.class})
public class BootstrapImportSelectorConfiguration {
    public BootstrapImportSelectorConfiguration() {
    }
}

加载BootstrapConfiguration配置的类,bootstrap容器的bean

public class BootstrapImportSelector implements EnvironmentAware, DeferredImportSelector {
    
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //加载BootstrapConfiguration注册bean
        List<String> names = new ArrayList(SpringFactoriesLoader.loadFactoryNames(BootstrapConfiguration.class, classLoader));
        names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(this.environment.getProperty("spring.cloud.bootstrap.sources", ""))));
        List<BootstrapImportSelector.OrderedAnnotatedElement> elements = new ArrayList();
        Iterator var5 = names.iterator();

        while(var5.hasNext()) {
            String name = (String)var5.next();

            try {
                elements.add(new BootstrapImportSelector.OrderedAnnotatedElement(this.metadataReaderFactory, name));
            } catch (IOException var8) {
            }
        }

        AnnotationAwareOrderComparator.sort(elements);
        String[] classNames = (String[])elements.stream().map((e) -> {
            return e.name;
        }).toArray((x$0) -> {
            return new String[x$0];
        });
        return classNames;
    }

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic 
@interface BootstrapConfiguration {   
 Class<?>[] exclude() default {};
}

其中PropertySourceBootstrapConfiguration 为spring cloud config加载处理类

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\

PropertySourceBootstrapConfiguration 实现 ApplicationContextInitializer接口 initialize函数加载配置

public class PropertySourceBootstrapConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    public void initialize(ConfigurableApplicationContext applicationContext) {
        CompositePropertySource composite = new OriginTrackedCompositePropertySource("bootstrapProperties");
        AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
        boolean empty = true;
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Iterator var5 = this.propertySourceLocators.iterator();
        
        while(var5.hasNext()) {
            PropertySourceLocator locator = (PropertySourceLocator)var5.next();
            PropertySource<?> source = null;
            source = locator.locate(environment);
            if (source != null) {
                logger.info("Located property source: " + source);
                composite.addPropertySource(source);
                empty = false;
            }
        }
        

在prepareContext applyInitializers触发

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    this.postProcessApplicationContext(context);
    this.applyInitializers(context);

PropertySourceLocator 接口

nacos和spring cloud config实现PropertySourceLocator接口

public interface PropertySourceLocator {
    PropertySource<?> locate(Environment environment);
}

spring cloud config 实现ConfigServicePropertySourceLocator类

public class ConfigServicePropertySourceLocator implements PropertySourceLocator {

加载

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration,\



public class ConfigServiceBootstrapConfiguration {
    @Bean
    @ConditionalOnMissingBean({ConfigServicePropertySourceLocator.class})
    @ConditionalOnProperty(
        value = {"spring.cloud.config.enabled"},
        matchIfMissing = true
    )
    public ConfigServicePropertySourceLocator configServicePropertySource(ConfigClientProperties properties) {
        ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(properties);
        return locator;
    }

nacos 实现 NacosPropertySourceLocator

public class NacosPropertySourceLocator implements PropertySourceLocator {

加载

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration



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

总结

1 PropertySourceLocator 接口是实现配置中心关键,开发者可以通过实现PropertySourceLocator 达到自定义配置

2 BootstrapConfiguration注解是bootstrap容器的扩展注册bean重要方式,通过spring.factories注册

3 通过配置中心加载过程可以了解到,spring boot 启动的时候,还有加载一个bootstrap容器的父容器,所以一些事件Listener监听器事件会触发两次。