spring cloud config配置中心

6 篇文章 0 订阅

Spring-Cloud-Config配置中心

Spring Cloud Config为分布式系统中的外部配置提供服务器和客户端支持。使用Config Server,您可以在所有环境中管理应用程序的外部属性。客户端和服务器上的概念映射与Spring EnvironmentPropertySource抽象相同,因此它们与Spring应用程序非常契合,但可以与任何以任何语言运行的应用程序一起使用。随着应用程序通过从开发人员到测试和生产的部署流程,您可以管理这些环境之间的配置,并确定应用程序具有迁移时需要运行的一切。服务器存储后端的默认实现使用git,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具。可以轻松添加替代实现,并使用Spring配置将其插入。

spring cloud config基于http协议的远程配置。为多个文件提供配置服务.通过统一的配置管理服务器进行配置管理,客户端通过https协议主动的拉取服务的的配置信息,完成配置.

spring cloud config可以将配置文件存储在本地,也可以将配置文件存储在git仓库.本文以配置本地文件为例,通过创建Config Server,通过他管理所有配置文件.

本测试实例使用依赖:

Spring Cloud: Finchley.SR2
Spring Cloud Config: 2.0.2.RELEASE
Spring Cloud Bus: 2.0.0.RELEASE
Spring Boot: 2.0.8.RELEASE

注: 依赖版本不能瞎导,否则会有改不完的bug.

Config Server 服务端

  • 创建maven工程,pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.config.demo</groupId>
        <artifactId>config-demo</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
        <modules>
            <module>config-server</module>
            <module>config-client</module>
        </modules>
    
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.8.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <!-- 管理依赖  -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Finchley.SR2</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    </project>
    
    • configServer的配置文件application.yml
#服务端口
server.port=8091
#服务名称
spring.application.name=configServer
#服务注册中心
#eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
#--------------------------------------------------------------------------------
#服务的git仓库地址
#spring.cloud.config.server.git.uri=https://github.com/huyilong1995/bdms-service.git
#配置文件拉去到本地的目录位置
#spring.cloud.config.server.git.basedir=target/config
#配置文件所在的目录
#spring.cloud.config.server.git.search-paths=/**
#git仓库的用户名
#spring.cloud.config.username=
#git仓库的密码
#spring.cloud.config.password=
#--------------------------------------------------------------------------------
#本地化配置file/classpath
spring.profiles.active=native
spring.cloud.config.server.native.search-locations=file:/C:/Users/HIISO/Desktop/config
#spring.cloud.config.server.native.search-locations=classpath:/config
#--------------------------------------------------------------------------------
#使用svn作为配置仓库,必须显示声明profiles.active=subversion
#spring.profiles.active=subversion
#spring.cloud.config.server.svn.uri=https://192.168.9.56/svn/Config_Files/
#spring.cloud.config.server.svn.username=
#spring.cloud.config.server.svn.password=
#spring.cloud.config.server.svn.search-paths=/**
#spring.cloud.config.server.svn.default-label=config
#--------------------------------------------------------------------------------
#rabbitmq配置,用于配置的动态刷新
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

management.endpoints.web.exposure.include=bus-refresh
#是否开启基本的鉴权,默认为true
security.basic.enabled=false
management.security.enabled=false

service.config.ip=127.0.0.1
  • client端拿取得配置文件,本文放在C:/Users/HIISO/Desktop/config下,有两个一个application-test.properties,一个是serviceA-test.properties.

    application-test.properties:

server.port=8081

​ service-test.properties:

age: 28
name: zhangsan
sex: women
  • 创建后启动类ConfigServerApplication.java.服务器为外部配置(名称值对或等效的YAML内容)提供了基于资源的HTTP。服务器可以使用@EnableConfigServer注释轻松嵌入到Spring Boot应用程序中。
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class,args);
    }
}

启动configServer服务,访问http://localhost:8091/serviceA-test.properties,显示结果如下:

age: 28
name: zhangsan
sex: women

证明配置服务中心可以从远程程序获取配置信息.

http请求地址和资源文件映射如下:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

{application} : 客户端properties文件中配置的spring.cloud.config.name,可配置多个,使用","隔开

{profile} : 客户端properties文件中配置的spring.cloud.config.profile

{label} : 客户端properties文件中配置的spring.cloud.config.label

Config Client 客户端

  • 创建maven工程.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>config-demo</artifactId>
        <groupId>com.config.demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>config-client</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.0.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.0.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.0.8.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  • 创建bootstrap.yml,配置读取本地配置中心的信息
#注意客户端里是没有配置服务的端口的,我们会从仓库中加载application.properties,那里配置了端口
spring.application.name=configClient

#对应前配置文件中的{application}部分,这样会加载多个配置文件,注意的是不同的配置文件里有相同的key会造成属性覆盖
spring.cloud.config.name=application,serviceA

#对应前配置文件中的{profile}部分
spring.cloud.config.enabled=true
spring.cloud.config.profile=test

#配置仓库的分支
#spring.cloud.config.label=config1
#配置中心服务端的地址
spring.cloud.config.uri=http://localhost:8091/

#rabbitmq配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

#bus的刷新路径为/bus-refresh
management.endpoints.web.exposure.include=bus-refresh

#启动失败时能够快速响应
spring.cloud.config.fail-fast=true
  • 创建启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ConfigClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication.class,args);
    }
}
  • 写一个handle读取配置信息
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RefreshScope
@RestController
public class TestController {

    @Value("${name}")
    private String name;
    @Value("${age}")
    private String age;
    @Value("${sex}")
    private String sex;

    @RequestMapping("age")
    public String age() {
        return age;
    }
    @RequestMapping("sex")
    public String sex() {
        return sex;
    }
    @RequestMapping("name")
    public String name() {
        return name;
    }
}

请求localhost:8081/sex

​ 页面显示:women.

注意:

1、注意客户端里是没有配置服务的端口的,我们会从仓库中加载application.properties,那里配置了端口

2、可以这样spring.cloud.config.name=application,serviceA 加载多个配置文件,注意的是不同的配置文件里有相同的key会造成属性覆盖,后面会覆盖前面的配置

3、spring.cloud.config.label=config1 指定了配置仓库的分支,比如Git的默认分支伟master,这里是分文件夹管理配置

关于spring-cloud-config的刷新功能

  • 导入spring-cloud-starter-bus-amqp和spring-boot-starter-actuator包(),具体版本上面已写出
  • 本地配置rabbitMQ环境并启动
  • 在我们需要刷新的配置类上加上@RefreshScope
  • propereties配置文件中加上 management.endpoints.web.exposure.include=bus-refresh(spring-boot2.0之后)
  • 调用服务端 http://localhost:8091/bus/refresh 接口刷新配置

这边以配置文件放在本地为例,SVN/本地化配置与Git不相同的是,Git可以配置webhook,当git端配置发生改变,自动调用/bus/refresh接口刷新配置,可以达到真正的自动化配置。完成上述五部即可半自动完成刷新配置


下面我们使SVN和本地化配置达到像Git一样的自动化配置,无需手动调用/bus/refresh接口.

思路: 可以利用commons-io,服务端一启动就开始监听配置仓库的文件,文件发生变化就自动发送一个post请求调用bus/refresh接口,达到自动刷新的效果

  • 在服务端导入依赖
	   <!--commons-io包中有FileAlterationListenerAdaptor类可以监听文件的改动-->
	   <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
		<!--发送http请求刷新配置文件-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
  • 创建ConfigurationListener继承FileAlterationListenerAdaptor
package com.config.server.listenner;

/**
 * @author Shen TiePeng
 * @description
 * @Date 2019/11/12 23:53
 */
import java.io.File;
import javax.annotation.PostConstruct;

import com.config.server.config.ConfigService;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ConfigurationListener extends FileAlterationListenerAdaptor {

    private static ConfigurationListener configurationListener;
    @Autowired
    private ConfigService configService;
    @PostConstruct
    public void init() {
        configurationListener = this;
        configurationListener.configService = this.configService;
    }

    public File DirContext;

    public ConfigurationListener() {
        super();
    }

    public ConfigurationListener(File dirContext) {
        super();
        DirContext = dirContext;
    }
	//监听文件夹创建
    @Override
    public void onDirectoryCreate(File directory) {
        configurationListener.configService.refreshConfig();

    }
	//监听文件夹改变
    @Override
    public void onDirectoryChange(File directory) {
        configurationListener.configService.refreshConfig();
    }
	//监听文件夹删除
    @Override
    public void onDirectoryDelete(File directory) {
        configurationListener.configService.refreshConfig();
    }
	//监听文件创建
    @Override
    public void onFileCreate(File file) {
        configurationListener.configService.refreshConfig();
    }
	//监听文件改变
    @Override
    public void onFileChange(File file) {
        configurationListener.configService.refreshConfig();
    }
	//监听文件删除
    @Override
    public void onFileDelete(File file) {
        configurationListener.configService.refreshConfig();
    }
}

  • 创建DsServiceInit实现ApplicationRunner(一启动就开始监听配置仓库的文件)
package com.config.server.listenner;

/**
 * @author Shen TiePeng
 * @description
 * @Date 2019/11/12 23:48
 */
import java.io.File;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;


@Component
public  class DsServiceInit implements ApplicationRunner {

    @Value("${spring.cloud.config.server.native.search-locations}")
    private String monitorPath;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        //截取要监听的仓库路径
        String path = StringUtils.substring(monitorPath, 6);
        File dir = new File(path);
        FileAlterationMonitor monitor = new FileAlterationMonitor();
        IOFileFilter filter = FileFilterUtils.or(FileFilterUtils.directoryFileFilter(),
                FileFilterUtils.fileFileFilter());
        FileAlterationObserver observer = new FileAlterationObserver(dir, filter);
        observer.addListener(new ConfigurationListener(dir));
        monitor.addObserver(observer);
        try {
            monitor.start();
            System.out.println("启动配置文件监听……");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  • 创建ConfigService(发送http请求)
package com.config.server.config;

/**
 * @author Shen TiePeng
 * @description
 * @Date 2019/11/12 23:54
 */

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ConfigService {

    //这里是自己定义的服务端的ip地址
    @Value("${service.config.ip}")
    private String serverIP;
    @Value("${server.port}")
    private String port;

    public void refreshConfig() {
        CloseableHttpClient httpClient = getHttpClient();
        try {
            String url = "http://" + serverIP + ":" + port + "/actuator/bus-refresh";
            HttpPost post = new HttpPost(url);
            post.setHeader("Content-type", "application/json");
            post.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            System.out.println("send POST 请求...." + post.getURI());
            CloseableHttpResponse httpResponse = httpClient.execute(post);
            try {
                org.apache.http.HttpEntity entity = httpResponse.getEntity();
                if (null != entity) {
                    System.out.println("-------------------------------------------------------");
                    System.out.println(EntityUtils.toString(entity, "UTF-8"));
                }
            } finally {
                httpResponse.close();
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                closeHttpClient(httpClient);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static CloseableHttpClient getHttpClient() {
        return HttpClients.createDefault();
    }

    public static void closeHttpClient(CloseableHttpClient client) throws IOException {
        if (client != null) {
            client.close();
        }
    }

}

这样就完成了配置仓库的监听以及自动刷新的功能,当有配置文件发生变化,服务端会自动发生post请求刷新配置而不需要我们再去手动刷新了

spring-cloud-config server的源码分析

  • 首先,查看@EnableConfigServer注解,看到源码里装配了ConfigServerConfiguration的bean.源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigServerConfiguration.class)
public @interface EnableConfigServer {

}
  • 看下ConfigServerConfiguration类,引入了Maker类,Marker唯一被引用的地方在ConfigServerAutoConfiguration类
@Configuration
public class ConfigServerConfiguration {
   class Marker {}

   @Bean
   public Marker enableConfigServerMarker() {
      return new Marker();
   }
}
  • ConfigServerAutoConfiguration类如下,@ConditionalOnBean(ConfigServerConfiguration.Marker.class)表示当装配了ConfigServerConfiguration.Marker的实例时才会执行ConfigServerAutoConfiguration的处理。这边主要看EnvironmentRepositoryConfiguration类.
@Configuration
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
@EnableConfigurationProperties(ConfigServerProperties.class)
@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,
      ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class })
public class ConfigServerAutoConfiguration {

}
  • EnvironmentRepositoryConfiguration类如下,@Import又引入了7种配置类,刚好对应config server的几种实现方式git的实现方式使用的配置类就是GitRepositoryConfiguration。
@Configuration
@EnableConfigurationProperties({ SvnKitEnvironmentProperties.class,
      JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class })
@Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultRepositoryConfiguration.class,
      SvnRepositoryConfiguration.class, NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class,
      DefaultRepositoryConfiguration.class })
public class EnvironmentRepositoryConfiguration {
}
  • GitRepositoryConfiguration继承DefaultRepositoryConfiguration ,可以看出GitRepositoryConfiguration是默认的实现方式,DefaultRepositoryConfiguration代码如下,最终是装配一个MultipleJGitEnvironmentRepository的bean,都实现了EnvironmentRepository类,使用findOne方法来查询配置。
@Configuration
@ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT)
class DefaultRepositoryConfiguration {
   @Autowired
   private ConfigurableEnvironment environment;

   @Autowired
   private ConfigServerProperties server;

   @Autowired(required = false)
   private TransportConfigCallback customTransportConfigCallback;

   @Bean
   public MultipleJGitEnvironmentRepository defaultEnvironmentRepository(
           MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory,
         MultipleJGitEnvironmentProperties environmentProperties) throws Exception {
      return gitEnvironmentRepositoryFactory.build(environmentProperties);
   }
}
  • findOne方法在EnvironmentController中的labelled方法中用到,这里的EnvironmentController#repository属性就是GitRepositoryConfiguration实例化的MultipleJGitEnvironmentRepository,如果是别的实现方式就是别的EnvironmentRepository。可以看出”/{name}/{profiles}/{label:.*}”路径参数正好与我们的请求方式相对应,因此Config Server是通过建立一个RestController来接收读取配置请求的,然后使用EnvironmentRepository来进行配置查询,返回一个org.springframework.cloud.config.environment.Environment对象的json串,推测客户端接收时也应该是反序列化为org.springframework.cloud.config.environment.Environment的一个实例。可以看一下Environment的属性定义。
@RequestMapping("/{name}/{profiles}/{label:.*}")
public Environment labelled(@PathVariable String name, @PathVariable String profiles,
      @PathVariable String label) {
   if (name != null && name.contains("(_)")) {
      // "(_)" is uncommon in a git repo name, but "/" cannot be matched
      // by Spring MVC
      name = name.replace("(_)", "/");
   }
   if (label != null && label.contains("(_)")) {
      // "(_)" is uncommon in a git branch name, but "/" cannot be matched
      // by Spring MVC
      label = label.replace("(_)", "/");
   }
   //findOne方法
   Environment environment = this.repository.findOne(name, profiles, label);
   if(!acceptEmpty && (environment == null || environment.getPropertySources().isEmpty())){
       throw new EnvironmentNotFoundException("Profile Not found");
   }
   return environment;
}
  • Environment属性如下:
private String name;

private String[] profiles = new String[0];

private String label;

private List<PropertySource> propertySources = new ArrayList<>();

private String version;

private String state;

spring-cloud-config client的源码分析

  • server源码中配置服务器rest接口返回的是Environment的json串,那么client这边反序列化应该也是Environment,搜索spring-cloud-config-client包使用Environment的地方,发现getRemoteEnvironment方法,getRemoteEnvironment的代码主要操作就是拼接一个请求配置地址串,获取所需的ApplicationName,profile,label参数,利用RestTemplate执行http请求,返回的json反序列化为Environment,从而获得所需要的配置信息
private Environment getRemoteEnvironment(RestTemplate restTemplate,
      ConfigClientProperties properties, String label, String state) {
   String path = "/{name}/{profile}";
   String name = properties.getName();
   String profile = properties.getProfile();
   String token = properties.getToken();
   int noOfUrls = properties.getUri().length;
   if (noOfUrls > 1) {
      logger.info("Multiple Config Server Urls found listed.");
   }

   Object[] args = new String[] { name, profile };
   if (StringUtils.hasText(label)) {
      if (label.contains("/")) {
         label = label.replace("/", "(_)");
      }
      args = new String[] { name, profile, label };
      path = path + "/{label}";
   }
   ResponseEntity<Environment> response = null;

   for (int i = 0; i < noOfUrls; i++) {
      Credentials credentials = properties.getCredentials(i);
      String uri = credentials.getUri();
      String username = credentials.getUsername();
      String password = credentials.getPassword();

      logger.info("Fetching config from server at : " + uri);

      try {
         HttpHeaders headers = new HttpHeaders();
         addAuthorizationToken(properties, headers, username, password);
         if (StringUtils.hasText(token)) {
            headers.add(TOKEN_HEADER, token);
         }
         if (StringUtils.hasText(state) && properties.isSendState()) {
            headers.add(STATE_HEADER, state);
         }

         final HttpEntity<Void> entity = new HttpEntity<>((Void) null, headers);
         response = restTemplate.exchange(uri + path, HttpMethod.GET, entity,
               Environment.class, args);
      }
      catch (HttpClientErrorException e) {
         if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
            throw e;
         }
      }
      catch (ResourceAccessException e) {
         logger.info("Connect Timeout Exception on Url - " + uri
               + ". Will be trying the next url if available");
         if (i == noOfUrls - 1)
            throw e;
         else
            continue;
      }

      if (response == null || response.getStatusCode() != HttpStatus.OK) {
         return null;
      }

      Environment result = response.getBody();
      return result;
   }

   return null;
}
  • client是在什么时候调用getRemoteEnvironment方法的?在getRemoteEnvironment方法上打断点,调用链路如下

    org.springframework.boot.SpringApplication#run(java.lang.String…)

    org.springframework.boot.SpringApplication#prepareContext

    org.springframework.boot.SpringApplication#applyInitializers

    org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration#initialize

    org.springframework.cloud.config.client.ConfigServicePropertySourceLocator#locate

    org.springframework.cloud.config.client.ConfigServicePropertySourceLocator#getRemoteEnvironment

  • 由调用链路可以知道在spring启动时就会远程加载配置信息,会遍历所有initializer进行一遍操作,PropertySourceBootstrapConfiguration就是其中之一的initializer

protected void applyInitializers(ConfigurableApplicationContext context) {
   for (ApplicationContextInitializer initializer : getInitializers()) {
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
            initializer.getClass(), ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      initializer.initialize(context);
   }
}
  • 当引入了spring-cloud-config后PropertySourceBootstrapConfiguration#propertySourceLocators中会新增一个ConfigServicePropertySourceLocator实例。在PropertySourceBootstrapConfiguration#initialize中遍历propertySourceLocators的locate方法,然后读取远程服务配置信息;如果没有引入了spring-cloud-config,那么propertySourceLocators将会是空集合。
@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(applicationContext, environment);
      handleIncludedProfiles(environment);
   }
}
  • propertySourceLocators是直接注入上下文中管理的PropertySourceLocator实例,搜索ConfigServicePropertySourceLocator,发现configServicePropertySource方法装配了一个ConfigServicePropertySourceLocator的bean
@Autowired(required = false)
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
@Configuration
@EnableConfigurationProperties
public class ConfigServiceBootstrapConfiguration {

@Bean
@ConditionalOnMissingBean(ConfigServicePropertySourceLocator.class)
@ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true)
public ConfigServicePropertySourceLocator configServicePropertySource(ConfigClientProperties properties) {
   ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
         properties);
   return locator;
}
   //........ 
}
  • spring cloud config client包里面的spring.factories里面引入了ConfigServiceBootstrapConfiguration,就想sprintboot的自动装配一样,程序会自动加载spring.factories里面的配置类.自动实例化一个ConfigServicePropertySourceLocator.spring.factories的配置如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.config.client.ConfigClientAutoConfiguration

# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration,\
org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值