springCloudConfig获取远程配置
client端获取配置
springCloudConfig核心其实在于实现了一个PropertySourceLocator接口来进行获取远程配置的
java">@Order(0)
public class ConfigServicePropertySourceLocator implements PropertySourceLocator {@Override@Retryable(interceptor = "configServerRetryInterceptor")public org.springframework.core.env.PropertySource<?> locate(org.springframework.core.env.Environment environment) {ConfigClientProperties properties = this.defaultProperties.override(environment);CompositePropertySource composite = new OriginTrackedCompositePropertySource("configService");RestTemplate restTemplate = this.restTemplate == null? getSecureRestTemplate(properties) : this.restTemplate;Exception error = null;String errorBody = null;try {String[] labels = new String[] { "" };if (StringUtils.hasText(properties.getLabel())) {labels = StringUtils.commaDelimitedListToStringArray(properties.getLabel());}String state = ConfigClientStateHolder.getState();// Try all the labels until one worksfor (String label : labels) {// 获取远程配置,这里就是使用restTemplate访问spring.cloud.config.uri对应的配置中心server地址/{name}/{profile}/{label}Environment result = getRemoteEnvironment(restTemplate, properties,label.trim(), state);if (result != null) {log(result);// result.getPropertySources() can be null if using xmlif (result.getPropertySources() != null) {for (PropertySource source : result.getPropertySources()) {@SuppressWarnings("unchecked")Map<String, Object> map = translateOrigins(source.getName(),(Map<String, Object>) source.getSource());composite.addPropertySource(new OriginTrackedMapPropertySource(source.getName(),map));}}if (StringUtils.hasText(result.getState())|| StringUtils.hasText(result.getVersion())) {HashMap<String, Object> map = new HashMap<>();putValue(map, "config.client.state", result.getState());putValue(map, "config.client.version", result.getVersion());composite.addFirstPropertySource(new MapPropertySource("configClient", map));}return composite;}}errorBody = String.format("None of labels %s found", Arrays.toString(labels));}catch (HttpServerErrorException e) {error = e;if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders().getContentType())) {errorBody = e.getResponseBodyAsString();}}catch (Exception e) {error = e;}if (properties.isFailFast()) {throw new IllegalStateException("Could not locate PropertySource and the fail fast property is set, failing"+ (errorBody == null ? "" : ": " + errorBody),error);}return null;}}
那么PropertySourceLocator接口是何时被调用的呢?
在springBoot启动的时候执行SpringApplication构造器的时候设置了ApplicationListener监听器
java">public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 设置ApplicationListener监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}
其中监听器中有BootstrapApplicationListener,在该监听器中会加入一个ApplicationContextInitializer初始化器的实现类PropertySourceBootstrapConfiguration
java">Set target = new LinkedHashSet<>(application.getInitializers());
target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
而在applyInitializers(context)中会遍历initializers初始化器集合,调用initialize方法
java">for (ApplicationContextInitializer initializer : getInitializers()) {Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),ApplicationContextInitializer.class);Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");initializer.initialize(context);
}
在PropertySourceBootstrapConfiguration的initialize方法中会遍历propertySourceLocators集合,来执行PropertySourceLocator的locateCollection方法,这样就和springCloudConfig获取配置接上了,使用ConfigServicePropertySourceLocator类
server端获取配置
根据上边的代码可以看到已经请求到server端了,那么server端如何拿到配置呢?先找一下访问地址对应的controller,果然在EnvironmentController中找到了对应的地址
java">@RequestMapping(path = "/{name}/{profiles}/{label:.*}",produces = MediaType.APPLICATION_JSON_VALUE)
public Environment labelled(@PathVariable String name, @PathVariable String profiles,@PathVariable String label) {return getEnvironment(name, profiles, label, false);
}
这里会调用
java">Environment environment = this.repository.findOne(name, profiles, label,includeOrigin);
我们以git为例,也就是使用MultipleJGitEnvironmentRepository来进行获取
java">public Environment findOne(String application, String profile, String label,boolean includeOrigin) {for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {if (repository.matches(application, profile, label)) {for (JGitEnvironmentRepository candidate : getRepositories(repository,application, profile, label)) {try {if (label == null) {label = candidate.getDefaultLabel();}Environment source = candidate.findOne(application, profile,label, includeOrigin);if (source != null) {return source;}}catch (Exception e) {if (this.logger.isDebugEnabled()) {this.logger.debug("Cannot load configuration from " + candidate.getUri()+ ", cause: (" + e.getClass().getSimpleName()+ ") " + e.getMessage(),e);}continue;}}}}JGitEnvironmentRepository candidate = getRepository(this, application, profile,label);if (label == null) {label = candidate.getDefaultLabel();}if (candidate == this) {return super.findOne(application, profile, label, includeOrigin);}return candidate.findOne(application, profile, label, includeOrigin);
}
就简单点,假如是第一次进来,就不用看上边的遍历了,直接去获取
java">super.findOne(application, profile, label, includeOrigin);
这里调用的是AbstractScmEnvironmentRepository的方法,
java">public synchronized Environment findOne(String application, String profile,String label, boolean includeOrigin) {NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(), new NativeEnvironmentProperties());// 创建本地git仓库,从对应git上进行clone,pull等操作Locations locations = getLocations(application, profile, label);delegate.setSearchLocations(locations.getLocations());// 之后就可以从本地git仓库进行读取了Environment result = delegate.findOne(application, profile, "", includeOrigin);result.setVersion(locations.getVersion());result.setLabel(label);return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),getUri());
}
https://zhhll.icu/2021/框架/微服务/springcloud/配置中心/springCloudConfig/源码分析/1.springCloudConfig获取远程配置/