Nacos源码分析六、NacosConfigBootstrapConfiguration配置类

前文在分析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了:

Nacos源码分析六、NacosConfigBootstrapConfiguration配置类

看一下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);
}

将结果放进环境,然后重新初始化等操作。

上一篇:SpringBoot启动流程原理解析(二)


下一篇:SAP Spartacus 3.0部署在development environment上