1. 程式人生 > 其它 >Nacos原始碼分析六、NacosConfigBootstrapConfiguration配置類

Nacos原始碼分析六、NacosConfigBootstrapConfiguration配置類

技術標籤:NacosSpringCloudAlibabajavaspring cloud alibabanacos

前文在分析BootStrapApplicationListener時得到當引入nacos時,會載入NacosConfigBootstrapConfiguration配置類:

@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這個是nacos的bootstrap屬性類。

我們先看NacosConfigManager這個類:

public class NacosConfigManager {

   private static final Logger log = LoggerFactory.getLogger(NacosConfigManager.class);

   private static ConfigService service = null;

   private NacosConfigProperties nacosConfigProperties;

   public NacosConfigManager(NacosConfigProperties nacosConfigProperties) {
      this.nacosConfigProperties = nacosConfigProperties;
      // Compatible with older code in NacosConfigProperties,It will be deleted in the
      // future.
      createConfigService(nacosConfigProperties);
   }

   /**
    * Compatible with old design,It will be perfected in the future.
    */
   static ConfigService createConfigService(
         NacosConfigProperties nacosConfigProperties) {
      if (Objects.isNull(service)) {
         synchronized (NacosConfigManager.class) {
            try {
               if (Objects.isNull(service)) {
                  service = NacosFactory.createConfigService(
                        nacosConfigProperties.assembleConfigServiceProperties());
               }
            }
            catch (NacosException e) {
               log.error(e.getMessage());
               throw new NacosConnectionFailureException(
                     nacosConfigProperties.getServerAddr(), e.getMessage(), e);
            }
         }
      }
      return service;
   }

   public ConfigService getConfigService() {
      if (Objects.isNull(service)) {
         createConfigService(this.nacosConfigProperties);
      }
      return service;
   }

   public NacosConfigProperties getNacosConfigProperties() {
      return nacosConfigProperties;
   }

}

可以看到,主要是為了獲得ConfigService例項,這個和我們一開始使用的測試類程式碼基本一致。

nacosConfigProperties.assembleConfigServiceProperties()主要是一些初始化配置:

public Properties assembleConfigServiceProperties() {
   Properties properties = new Properties();
   properties.put(SERVER_ADDR, Objects.toString(this.serverAddr, ""));
   properties.put(USERNAME, Objects.toString(this.username, ""));
   properties.put(PASSWORD, Objects.toString(this.password, ""));
   properties.put(ENCODE, Objects.toString(this.encode, ""));
   properties.put(NAMESPACE, Objects.toString(this.namespace, ""));
   properties.put(ACCESS_KEY, Objects.toString(this.accessKey, ""));
   properties.put(SECRET_KEY, Objects.toString(this.secretKey, ""));
   properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, ""));
   properties.put(MAX_RETRY, Objects.toString(this.maxRetry, ""));
   properties.put(CONFIG_LONG_POLL_TIMEOUT,
         Objects.toString(this.configLongPollTimeout, ""));
   properties.put(CONFIG_RETRY_TIME, Objects.toString(this.configRetryTime, ""));
   properties.put(ENABLE_REMOTE_SYNC_CONFIG,
         Objects.toString(this.enableRemoteSyncConfig, ""));
   String endpoint = Objects.toString(this.endpoint, "");
   if (endpoint.contains(":")) {
      int index = endpoint.indexOf(":");
      properties.put(ENDPOINT, endpoint.substring(0, index));
      properties.put(ENDPOINT_PORT, endpoint.substring(index + 1));
   }
   else {
      properties.put(ENDPOINT, endpoint);
   }

   enrichNacosConfigProperties(properties);
   return properties;
}

然後是NacosPropertySourceLocator類,這個是nacos的屬性源定位器。我們先看一下什麼地方載入它的。

先回來看spring-cloud-context的spring.factories檔案定義:

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

關注PropertySourceBootstrapConfiguration這個配置類,他是用來做遠端配置處理的,這是個初始化器,在準備好上下文,重新整理前applyInitializers方法執行。

他有個注入屬性,我們已經注入了nacosNacosPropertySourceLocator了:

在這裡插入圖片描述

看一下initialize方法:

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
   List<PropertySource<?>> composite = new ArrayList<>();
   AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
   boolean empty = true;
   ConfigurableEnvironment environment = applicationContext.getEnvironment();
   for (PropertySourceLocator locator : this.propertySourceLocators) {
      Collection<PropertySource<?>> source = locator.locateCollection(environment);
      if (source == null || source.size() == 0) {
         continue;
      }
      List<PropertySource<?>> sourceList = new ArrayList<>();
      for (PropertySource<?> p : source) {
         sourceList.add(new BootstrapPropertySource<>(p));
      }
      logger.info("Located property source: " + sourceList);
      composite.addAll(sourceList);
      empty = false;
   }
   if (!empty) {
      MutablePropertySources propertySources = environment.getPropertySources();
      String logConfig = environment.resolvePlaceholders("${logging.config:}");
      LogFile logFile = LogFile.get(environment);
      for (PropertySource<?> p : environment.getPropertySources()) {
         if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
            propertySources.remove(p.getName());
         }
      }
      insertPropertySources(propertySources, composite);
      reinitializeLoggingSystem(environment, logConfig, logFile);
      setLogLevels(applicationContext, environment);
      handleIncludedProfiles(environment);
   }
}

先將屬性源定位器propertySourceLocators排序,然後遍歷進行定位,封裝成PropertySource並放進集合裡。

locateCollection方法:

default Collection<PropertySource<?>> locateCollection(Environment environment) {
   return locateCollection(this, environment);
}

static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator,
      Environment environment) {
   PropertySource<?> propertySource = locator.locate(environment);
   if (propertySource == null) {
      return Collections.emptyList();
   }
   if (CompositePropertySource.class.isInstance(propertySource)) {
      Collection<PropertySource<?>> sources = ((CompositePropertySource) propertySource)
            .getPropertySources();
      List<PropertySource<?>> filteredSources = new ArrayList<>();
      for (PropertySource<?> p : sources) {
         if (p != null) {
            filteredSources.add(p);
         }
      }
      return filteredSources;
   }
   else {
      return Arrays.asList(propertySource);
   }
}

locator.locate(environment)就進入了我們的NacosPropertySourceLocator實現中了。

@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;
		}
		//超時30秒
		long timeout = nacosConfigProperties.getTimeout();
		//屬性源建造器
		nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
				timeout);
		//dataid的名字
		String name = nacosConfigProperties.getName();

		//字首
		String dataIdPrefix = nacosConfigProperties.getPrefix();
		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;
	}

主要看一下loadApplicationConfiguration方法:

private void loadApplicationConfiguration(
      CompositePropertySource compositePropertySource, String dataIdPrefix,
      NacosConfigProperties properties, Environment environment) {
   //副檔名
   String fileExtension = properties.getFileExtension();
   //分組,預設DEFAULT_GROUP
   String nacosGroup = properties.getGroup();
   // load directly once by default 直接預設配置檔案載入一次
   loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
         fileExtension, true);
   // load with suffix, which have a higher priority than the default
   //檔名加字尾來一次
   loadNacosDataIfPresent(compositePropertySource,
         dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
   // Loaded with profile, which have a higher priority than the suffix
   //有環境配置的更高級別
   for (String profile : environment.getActiveProfiles()) {
      String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
      loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
            fileExtension, true);
   }

}

然後是loadNacosDataIfPresent方法:

private void loadNacosDataIfPresent(final CompositePropertySource composite,
      final String dataId, final String group, String fileExtension,
      boolean isRefreshable) {
   if (null == dataId || dataId.trim().length() < 1) {
      return;
   }
   if (null == group || group.trim().length() < 1) {
      return;
   }
   NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
         fileExtension, isRefreshable);
   this.addFirstPropertySource(composite, propertySource, false);
}

然後是loadNacosPropertySource方法:

private NacosPropertySource loadNacosPropertySource(final String dataId,
			final String group, String fileExtension, boolean isRefreshable) {
    //重新整理過了
    if (NacosContextRefresher.getRefreshCount() != 0) {
        if (!isRefreshable) {
            //不重新整理,直接快取取
            return NacosPropertySourceRepository.getNacosPropertySource(dataId,
                                                                        group);
        }
    }
    return nacosPropertySourceBuilder.build(dataId, group, fileExtension,
                                            isRefreshable);
}

第一次沒有快取,呼叫nacosPropertySourceBuilder.build方法:

NacosPropertySource build(String dataId, String group, String fileExtension,
      boolean isRefreshable) {
   Map<String, Object> p = loadNacosData(dataId, group, fileExtension);
   NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId,
         p, new Date(), isRefreshable);
   NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
   return nacosPropertySource;
}

先載入資料,然後結果封裝成NacosPropertySource,放進快取。

loadNacosData方法:

private Map<String, Object> loadNacosData(String dataId, String group,
      String fileExtension) {
   String data = null;
   try {
      data = configService.getConfig(dataId, group, timeout);
      if (StringUtils.isEmpty(data)) {
         log.warn(
               "Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
               dataId, group);
         return EMPTY_MAP;
      }
      if (log.isDebugEnabled()) {
         log.debug(String.format(
               "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
               group, data));
      }
      Map<String, Object> dataMap = NacosDataParserHandler.getInstance()
            .parseNacosData(data, fileExtension);
      return dataMap == null ? EMPTY_MAP : dataMap;
   }
   catch (NacosException e) {
      log.error("get data from Nacos error,dataId:{}, ", dataId, e);
   }
   catch (Exception e) {
      log.error("parse data from Nacos error,dataId:{},data:{},", dataId, data, e);
   }
   return EMPTY_MAP;
}

就是configService.getConfig的呼叫。

然後回來PropertySourceBootstrapConfiguration的initialize方法後半部分:

if (!empty) {
   MutablePropertySources propertySources = environment.getPropertySources();
   String logConfig = environment.resolvePlaceholders("${logging.config:}");
   LogFile logFile = LogFile.get(environment);
   for (PropertySource<?> p : environment.getPropertySources()) {
      if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
          //刪除bootstrapProperties,因為bootstrap已經處理完了
         propertySources.remove(p.getName());
      }
   }
    //將結果放入環境的MutablePropertySources中
   insertPropertySources(propertySources, composite);
    //重新初始化log系統
   reinitializeLoggingSystem(environment, logConfig, logFile);
    //設定log級別
   setLogLevels(applicationContext, environment);
    //處理包含的環境配置
   handleIncludedProfiles(environment);
}

將結果放進環境,然後重新初始化等操作。