nacos-config 配置數據加載


我們從SpringBoot的run方法入手,主體邏輯步驟為:

1.prepareEnvironment

2.createApplicationContext

3.prepareContext

4.refreshContext

5.afterRefresh

prepareEnvironment

org.springframework.boot.SpringApplication#run(java.lang.String...) =》org.springframework.boot.SpringApplication#prepareEnvironment =》org.springframework.boot.SpringApplicationRunListeners#environmentPrepared =》org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared

在這里通過org.springframework.context.event.SimpleApplicationEventMulticaster廣播ApplicationEnvironmentPreparedEvent事件。

那么,事件的監聽者有哪些?通過代碼走讀發現,事件的監聽者在EventPublishingRunListener構造方法中通過SpringApplication.getListeners()獲取。那么,SpringApplication的監聽者又是哪來的?

同樣,走讀代碼如下,通過springfactories機制加載org.springframework.context.ApplicationListener實現:

 這其中就有一個Spring Cloud擴展的實現:

 這個listener監聽的正是上文提到的環境准備事件ApplicationEnvironmentPreparedEvent:

 org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent主要通過構建臨時ApplicationContext,利用Spring自帶的IOC能力,查找以SpringFactories方式擴展org.springframework.cloud.bootstrap.BootstrapConfiguration注解的class,BootstrapApplicationListener主要目的是查找ApplicationContextInitializer並加入到SpringApplication的initializers列表中,用於監聽后續正式Context事件

代碼調用路徑:org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent =》 org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext

這里通過SpringApplicationBuilder構建臨時Context,build的source為org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration

 

 這里通過org.springframework.cloud.bootstrap.BootstrapImportSelector加載返回字符串對應的class實例到IOC容器。至於@Import注解的分析見另外一篇博文。

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
// 獲取通過SpringFactories方式以org.springframework.cloud.bootstrap.BootstrapConfiguration為key擴展的所有class類名
List<String> names = new ArrayList<>(SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader));
// 這里有個擴展點,可以通過spring.cloud.bootstrap.sources加載Bootstrap相關bean 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; }

這里有個跟本篇主題相關的Spring Cloud擴展BootstrapConfiguration的兩個實現:

 構建完context后回到onApplicationEvent方法,調用org.springframework.cloud.bootstrap.BootstrapApplicationListener#apply。

回到org.springframework.boot.SpringApplication#run(java.lang.String...)方法,

 prepareContext

了解spring源碼的人應該知道,bean的創建等都是在Context的refresh中。這里再refresh之前做些環境數據的准備工作。

 org.springframework.boot.SpringApplication#prepareContext =》org.springframework.boot.SpringApplication#applyInitializers

循環調用所有initializer的initialize方法:

 看下上文提到的Spring Cloud擴展org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration#initialize,重點代碼片段如下,通過propertySourceLocators獲取PropertySource:

 問題來了,propertySourceLocators是哪里來的?還記得PropertySourceBootstrapConfiguration是怎么構建的嗎?沒錯,就是以org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration為source,創建臨時Context加載生成的bean,這里會加載所有以org.springframework.cloud.bootstrap.BootstrapConfiguration為key的bean,我們的主角登場了,Spring Cloud Alibaba Nacos Config擴展了Spring Cloud的這個點:

 com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration自動創建了NacosPropertySourceLocator:

 最終會調用com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#locate:

@Override
    public PropertySource<?> locate(Environment env) {
        nacosConfigProperties.setEnvironment(env);
        ConfigService configService = nacosConfigManager.getConfigService();

        if (null == configService) {
            log.warn("no instance of config service found, can't load config from nacos");
            return null;
        }
        long timeout = nacosConfigProperties.getTimeout();
        nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
                timeout);
        String name = nacosConfigProperties.getName();
        // 優先使用配置的spring.cloud.nacos.config.prefix
        String dataIdPrefix = nacosConfigProperties.getPrefix();
// 如果沒有配置prefix,嘗試使用spring.cloud.nacos.config.name
if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = name; } // 還是沒有的話,使用spring.application.name if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = env.getProperty("spring.application.name"); } CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); // 加載共享配置 loadSharedConfiguration(composite);
// 加載擴展配置 loadExtConfiguration(composite);
// 加載應用配置 loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite; }

com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadApplicationConfiguration =》 com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosDataIfPresent =》com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosPropertySource =》com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#build =》com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData =》com.alibaba.nacos.client.config.NacosConfigService#getConfig

最終通過nacos client的configService從服務端獲取數據並封裝成PropertySource返回。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM