Spring Cloud Config 源码分析

作者: 数齐 | 来源:发表于2017-09-09 22:17 被阅读280次

    今天我们来分析一下spring cloud 的 config 的源码。我们可以看到spring-cloud-config-client项目下的spring.factories文件中定义了我们今天所要讲的文 ConfigClientAutoConfiguration 与ConfigServiceBootstrapConfiguration。他们被自动引入进我们的容器中作为Bean。我们先看一下ConfigServiceBootstrapConfiguration

    @Configuration
    @EnableConfigurationProperties
    public class ConfigServiceBootstrapConfiguration {
    
       @Autowired
       private ConfigurableEnvironment environment;
    
       ...
    
       @Bean
       @ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true)
       public ConfigServicePropertySourceLocator configServicePropertySource() {
          ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
                configClientProperties());
          return locator;
       }
    
       ...
    
    }
    

    我们说一下作为重要的一个Bean configServicePropertySource,他的作用就是从远程服务器上拿到我们的配置,放入到spring 容器中的
    environment 中。让我们详细的看一下。

    @Override
    @Retryable(interceptor = "configServerRetryInterceptor")
    public org.springframework.core.env.PropertySource<?> locate(
          org.springframework.core.env.Environment environment) {
       ConfigClientProperties client = this.defaults.override(environment);
       CompositePropertySource composite = new CompositePropertySource("configService");
       RestTemplate restTemplate = this.restTemplate == null ? getSecureRestTemplate(client)
             : this.restTemplate;
       Exception error = null;
       String errorBody = null;
       logger.info("Fetching config from server at: " + client.getRawUri());
       try {
          String[] labels = new String[]{""};
          if (StringUtils.hasText(client.getLabel())) {
             labels = StringUtils.commaDelimitedListToStringArray(client.getLabel());
          }
          // Try all the labels until one works
          for (String label : labels) {
             Environment result = getRemoteEnvironment(restTemplate, client.getRawUri(), client.getName(), client.getProfile(), label.trim());
             if (result != null) {
                logger.info(String.format("Located environment: name=%s, profiles=%s, label=%s, version=%s",
                      result.getName(),
                      result.getProfiles() == null ? "" : Arrays.asList(result.getProfiles()),
                      result.getLabel(), result.getVersion()));
    
                for (PropertySource source : result.getPropertySources()) {
                   @SuppressWarnings("unchecked")
                   Map<String, Object> map = (Map<String, Object>) source
                         .getSource();
                   composite.addPropertySource(new MapPropertySource(source
                         .getName(), map));
                }
                return composite;
             }
          }
       }
       catch (HttpServerErrorException e) {
          error = e;
          if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders()
                .getContentType())) {
             errorBody = e.getResponseBodyAsString();
          }
       }
       catch (Exception e) {
          error = e;
       }
       if (client != null && client.isFailFast()) {
          throw new IllegalStateException(
                "Could not locate PropertySource and the fail fast property is set, failing",
                error);
       }
       logger.warn("Could not locate PropertySource: "
             + (errorBody == null ? error==null ? "label not found" : error.getMessage() : errorBody));
       return null;
    
    }
     
     
    private Environment getRemoteEnvironment(RestTemplate restTemplate, String uri, String name, String profile, String label) {
       String path = "/{name}/{profile}";
       Object[] args = new String[] { name, profile };
       if (StringUtils.hasText(label)) {
          args = new String[] { name, profile, label };
          path = path + "/{label}";
       }
       ResponseEntity<Environment> response = null;
    
       try {
          response = restTemplate.exchange(uri + path,
                HttpMethod.GET, new HttpEntity<Void>((Void) null),
                Environment.class, args);
       } catch (HttpClientErrorException e) {
          if(e.getStatusCode() != HttpStatus.NOT_FOUND ) {
             throw e;
          }
       }
    
       if (response==null || response.getStatusCode()!=HttpStatus.OK) {
          return null;
       }
       Environment result = response.getBody();
       return result;
    }
    

    这个类的生命周期方法是locate,getRemoteEnvironment这个方法就是从远程服务器上拉取数据,将数据放入到Environment中返回,然后解析得到的Environment,将数据放入
    composite。在哪里用呢?请看PropertySourceBootstrapConfiguration

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
       CompositePropertySource composite = new CompositePropertySource(
             BOOTSTRAP_PROPERTY_SOURCE_NAME);
       AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
       boolean empty = true;
       ConfigurableEnvironment environment = applicationContext.getEnvironment();
       for (PropertySourceLocator locator : this.propertySourceLocators) {
          PropertySource<?> source = null;
          source = locator.locate(environment);
          if (source == null) {
             continue;
          }
          logger.info("Located property source: " + source);
          composite.addPropertySource(source);
          empty = false;
       }
       if (!empty) {
          MutablePropertySources propertySources = environment.getPropertySources();
          String logConfig = environment.resolvePlaceholders("${logging.config:}");
          LogFile logFile = LogFile.get(environment);
          if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
             propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
          }
          insertPropertySources(propertySources, composite);
          reinitializeLoggingSystem(environment, logConfig, logFile);
          setLogLevels(environment);
       }
    }
    

    首先这是一个ApplicationContextInitializer的子类,那么会在spring boot 进行初始化的时候调用,将所有PropertySourceLocator类型的对象的locate方法都调用一遍,然后将各个渠道得到的属性值放到
    composite中利用insertPropertySources(propertySources, composite)设置到environment中,这样容器就得到了。

    相关文章

      网友评论

        本文标题:Spring Cloud Config 源码分析

        本文链接:https://www.haomeiwen.com/subject/krwajxtx.html