作为配置中心是通过核心类ConsulConfigAutoConfigurationConsulConfigBootstrapConfiguration实现的。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-consul-config</artifactId>
</dependency>

1.ConsulConfigAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled", matchIfMissing = true)
public class ConsulConfigAutoConfiguration {

	/**
	 * Name of the config watch task scheduler bean.
	 */
	public static final String CONFIG_WATCH_TASK_SCHEDULER_NAME = "configWatchTaskScheduler";

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(RefreshEndpoint.class)
	protected static class ConsulRefreshConfiguration {

		@Bean
		@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled",
				matchIfMissing = true)
		public ConfigWatch configWatch(ConsulConfigProperties properties,
				ConsulPropertySourceLocator locator, ConsulClient consul,
				@Qualifier(CONFIG_WATCH_TASK_SCHEDULER_NAME) TaskScheduler taskScheduler) {
			return new ConfigWatch(properties, consul, locator.getContextIndexes(),
					taskScheduler);
		}

		@Bean(name = CONFIG_WATCH_TASK_SCHEDULER_NAME)
		@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled",
				matchIfMissing = true)
		public TaskScheduler configWatchTaskScheduler() {
			return new ThreadPoolTaskScheduler();
		}

	}

}

2.ConsulConfigBootstrapConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {

	@Configuration(proxyBeanMethods = false)
	@EnableConfigurationProperties
	@Import(ConsulAutoConfiguration.class)
	@ConditionalOnProperty(name = "spring.cloud.consul.config.enabled",matchIfMissing = true)
	protected static class ConsulPropertySourceConfiguration {

		@Autowired
		private ConsulClient consul;

		@Bean
		@ConditionalOnMissingBean
		public ConsulConfigProperties consulConfigProperties() {
			return new ConsulConfigProperties();
		}

		@Bean
		public ConsulPropertySourceLocator consulPropertySourceLocator(
				ConsulConfigProperties consulConfigProperties) {
			return new ConsulPropertySourceLocator(this.consul, consulConfigProperties);
		}
	}
}

3.ConsulPropertySourceLocator

public class ConsulPropertySourceLocator implements PropertySourceLocator {
	private final List<String> contexts = new ArrayList<>();
	public PropertySource<?> locate(Environment environment) {
		if (environment instanceof ConfigurableEnvironment) {
			ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
			String appName = this.properties.getName();
			if (appName == null) {//name属性 如果没有显式配置,则选择spring.application.name
				appName = env.getProperty("spring.application.name");
			}
			List<String> profiles = Arrays.asList(env.getActiveProfiles());
			String prefix = this.properties.getPrefix();//默认值为 config
			List<String> suffixes = new ArrayList<>();
			if (this.properties.getFormat() != FILES) {
				suffixes.add("/");
			}else {
				suffixes.add(".yml");
				suffixes.add(".yaml");
				suffixes.add(".properties");
			}
			//defaultContext:config/application
			String defaultContext = getContext(prefix,this.properties.getDefaultContext());
			for (String suffix : suffixes) {
				this.contexts.add(defaultContext + suffix);// config/application/
			}
			for (String suffix : suffixes) {// config/application,dev/
				addProfiles(this.contexts, defaultContext, profiles, suffix);
			}
			String baseContext = getContext(prefix, appName);// config/appName
			for (String suffix : suffixes) {
				this.contexts.add(baseContext + suffix);// config/appName/
			}
			for (String suffix : suffixes) {
				addProfiles(this.contexts, baseContext, profiles, suffix);// config/appName,dev/
			}
			Collections.reverse(this.contexts);
			CompositePropertySource composite = new CompositePropertySource("consul");
			for (String propertySourceContext : this.contexts) {
				try {// config/application、config/application,profiles/、config/appName、config/appName,profiles/
					ConsulPropertySource propertySource = null;
					if (this.properties.getFormat() == FILES) {
						Response<GetValue> response = this.consul.getKVValue(
								propertySourceContext, this.properties.getAclToken());
						addIndex(propertySourceContext, response.getConsulIndex());
						if (response.getValue() != null) {
							ConsulFilesPropertySource filesPropertySource = new ConsulFilesPropertySource(
									propertySourceContext, this.consul, this.properties);
							filesPropertySource.init(response.getValue());
							propertySource = filesPropertySource;
						}
					}
					else {
						propertySource = create(propertySourceContext, this.contextIndex);
					}
					if (propertySource != null) {
						composite.addPropertySource(propertySource);
					}
				}
			}
	
			return composite;
		}
		return null;
	}
	
	private ConsulPropertySource create(String context, Map<String, Long> contextIndex) {
		ConsulPropertySource propertySource = new ConsulPropertySource(context,
				this.consul, this.properties);
		propertySource.init();
		addIndex(context, propertySource.getInitialIndex());
		return propertySource;
	}
}

public class ConsulPropertySource extends EnumerablePropertySource<ConsulClient> {

	private final Map<String, Object> properties = new LinkedHashMap<>();

	private String context;

	private ConsulConfigProperties configProperties;

	private Long initialIndex;

	public void init() {
		if (!this.context.endsWith("/")) {
			this.context = this.context + "/";
		}
		// 如果kv方式,通过http方式请求接口 /v1/kv/config/consul-producer,dev/
		Response<List<GetValue>> response = this.source.getKVValues(this.context,
				this.configProperties.getAclToken(), QueryParams.DEFAULT);
	
		this.initialIndex = response.getConsulIndex();
	
		final List<GetValue> values = response.getValue();
		ConsulConfigProperties.Format format = this.configProperties.getFormat();
		switch (format) {
		case KEY_VALUE:
			parsePropertiesInKeyValueFormat(values);
			break;
		case PROPERTIES:
		case YAML:
			parsePropertiesWithNonKeyValueFormat(values, format);
		}
	}

	protected void parsePropertiesInKeyValueFormat(List<GetValue> values) {
		if (values == null) {
			return;
		}
	
		for (GetValue getValue : values) {
			String key = getValue.getKey();// key = config/appName,dev/key
			if (!StringUtils.endsWithIgnoreCase(key, "/")) {// 如果key的后缀没有"/"表明是配置的kv,并非是目录
				key = key.replace(this.context, "").replace('/', '.');// 截取得到最终应用中的key
				String value = getValue.getDecodedValue();
				this.properties.put(key, value);
			}
		}
	}

	protected void parsePropertiesWithNonKeyValueFormat(List<GetValue> values,ConsulConfigProperties.Format format) {
		if (values == null) {
			return;
		}

		for (GetValue getValue : values) {
			// 从key中截取config/appName,dev/data,最终得到key = data
			String key = getValue.getKey().replace(this.context, "");
			//判断key = data 是否等于 ConsulConfigProperties中dataKey值默认为data,注意data不能是目录,即存在后缀"/"
			if (this.configProperties.getDataKey().equals(key)) {
				parseValue(getValue, format);
			}
		}
	}

	protected void parseValue(GetValue getValue, ConsulConfigProperties.Format format) {
		String value = getValue.getDecodedValue();//获取到yaml配置的所有kv
		if (value == null) {
			return;
		}
		Properties props = generateProperties(value, format);

		for (Map.Entry entry : props.entrySet()) {
			this.properties.put(entry.getKey().toString(), entry.getValue());
		}
	}
	//因为value是配置中心全部的配置信息,并且是字符串类型,所以需要解析字符串
	protected Properties generateProperties(String value,ConsulConfigProperties.Format format) {
		final Properties props = new Properties();
		if (format == PROPERTIES) {// 如果项目中配置的是PROPERTIES,即配置中心配置方式是k = v,则通过"="进行解析
			props.load(new ByteArrayInputStream(value.getBytes("ISO-8859-1")));
			return props;
		}
		else if (format == YAML) {//如果项目中配置的是YAML,即配置中心配置方式是k : v,则通过":"进行解析
			final YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
			yaml.setResources(new ByteArrayResource(value.getBytes(Charset.forName("UTF-8"))));
			return yaml.getObject();
		}

		return props;
	}

}

配置中心存在四种方式的配置形式即JSON、yaml、HCL & xml。

默认即spring.cloud.consul.config.format=KEY_VALUE,如下所示:
在这里插入图片描述
如果kv方式则配置中心的配置方式没有效果,kv配置方式如上图所示。

spring.cloud.consul.config.format=yaml或者PROPERTIES,且配置中心如下所示:
在这里插入图片描述
如果yaml || PROPERTIES 方式则配置中心的配置方式选择对应的yaml,yaml配置方式如图所示。

4.ConfigWatch

存在一个定时任务,感知配置文件信息的变化。如果存在配置信息变更则触发RefreshEvent类型的事件,并且由RefreshEventListener监听器处理该事件。核心逻辑删除scopedTarget.xxx对应的bean实例缓存信息

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐