Spring Cloud 覆写远端的配置属性实例详解

应用的配置源通常都是远端的Config Server服务器,默认情况下,本地的配置优先级低于远端配置仓库。如果想实现本地应用的系统变量和config文件覆盖远端仓库中的属性值,可以通过如下设置:

spring:
cloud:
config:
allowOverride: true
overrideNone: true
overrideSystemProperties: false
  • overrideNone:当allowOverride为true时,overrideNone设置为true,外部的配置优先级更低,而且不能覆盖任何存在的属性源。默认为false
  • allowOverride:标识overrideSystemProperties属性是否启用。默认为true,设置为false意为禁止用户的设置
  • overrideSystemProperties:用来标识外部配置是否能够覆盖系统属性,默认为true

客户端通过如上配置,可以实现本地配置优先级更高,且不能被覆盖。由于我们基于的Spring Cloud当前版本是 Edgware.RELEASE ,上面的设置并不能起作用,而是使用了 PropertySourceBootstrapProperties 中的默认值。具体情况见issue: https://github.com/spring-cloud/spring-cloud-commons/pull/250 ,我们在下面分析时会讲到具体的bug源。

源码分析

ConfigServicePropertySourceLocator

覆写远端的配置属性归根结底与客户端的启动时获取配置有关,在获取到配置之后如何处理?我们看一下spring cloud config中的资源获取类 ConfigServicePropertySourceLocator 的类图。

ConfigServicePropertySourceLocator 实质是一个属性资源定位器,其主要方法是 locate(Environment environment) 。首先用当前运行应用的环境的application、profile和label替换configClientProperties中的占位符并初始化RestTemplate,然后遍历labels数组直到获取到有效的配置信息,最后还会根据是否快速失败进行重试。主要流程如下:

locate(Environment environment) 调用 getRemoteEnvironment(restTemplate, properties, label, state) 方法通过http的方式获取远程服务器上的配置数据。实现也很简单,显示替换请求路径path中占位符,然后进行头部headers组装,组装好了就可以发送请求,最后返回结果。

在上面的实现中,我们看到获取到的配置信息存放在 CompositePropertySource ,那是如何使用它的呢?这边补充另一个重要的类是PropertySourceBootstrapConfiguration,它实现了ApplicationContextInitializer接口,该接口会在应用上下文刷新之前 refresh() 被回调,从而执行初始化操作,应用启动后的调用栈如下:

SpringApplicationBuilder.run() -> SpringApplication.run() -> SpringApplication.createAndRefreshContext() -> SpringApplication.applyInitializers() -> PropertySourceBootstrapConfiguration.initialize()
PropertySourceBootstrapConfiguration

而上述 ConfigServicePropertySourceLocator 的locate方法会在initialize中被调用,从而保证上下文在刷新之前能够拿到必要的配置信息。具体看一下initialize方法:

public class PropertySourceBootstrapConfigurationimplements
 ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
 private int order = Ordered.HIGHEST_PRECEDENCE + 10;
 @Autowired(required = false)
 private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
 @Override
 public void initialize(ConfigurableApplicationContext applicationContext){
 CompositePropertySource composite = new CompositePropertySource(
 BOOTSTRAP_PROPERTY_SOURCE_NAME);
 //对propertySourceLocators数组进行排序,根据默认的AnnotationAwareOrderComparator
 AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
 boolean empty = true;
 //获取运行的环境上下文
 ConfigurableEnvironment environment = applicationContext.getEnvironment();
 for (PropertySourceLocator locator : this.propertySourceLocators) {
 //遍历this.propertySourceLocators
 PropertySource<?> source = null;
 source = locator.locate(environment);
 if (source == null) {
 continue;
 }
 logger.info("Located property source: " + source);
 //将source添加到PropertySource的链表中
 composite.addPropertySource(source);
 empty = false;
 }
 //只有source不为空的情况,才会设置到environment中
 if (!empty) {
 //返回Environment的可变形式,可进行的操作如addFirst、addLast
 MutablePropertySources propertySources = environment.getPropertySources();
 String logConfig = environment.resolvePlaceholders("${logging.config:}");
 LogFile logFile = LogFile.get(environment);
 if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
 //移除bootstrapProperties
 propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
 }
 //根据config server覆写的规则,设置propertySources
 insertPropertySources(propertySources, composite);
 reinitializeLoggingSystem(environment, logConfig, logFile);
 setLogLevels(environment);
 //处理多个active profiles的配置信息
 handleIncludedProfiles(environment);
 }
 }
 //...
}

下面我们看一下,在 initialize 方法中进行了哪些操作。

  • 根据默认的 AnnotationAwareOrderComparator 排序规则对propertySourceLocators数组进行排序
  • 获取运行的环境上下文ConfigurableEnvironment
  • 遍历propertySourceLocators时
  • 调用 locate 方法,传入获取的上下文environment
  • 将source添加到PropertySource的链表中
  • 设置source是否为空的标识标量empty
  • source不为空的情况,才会设置到environment中

返回Environment的可变形式,可进行的操作如addFirst、addLast

移除propertySources中的bootstrapProperties

根据config server覆写的规则,设置propertySources

处理多个active profiles的配置信息

初始化方法 initialize 处理时,先将所有PropertySourceLocator类型的对象的 locate 方法遍历,然后将各种方式得到的属性值放到CompositePropertySource中,最后调用 insertPropertySources(propertySources, composite) 方法设置到Environment中。Spring Cloud Context中提供了覆写远端属性的 PropertySourceBootstrapProperties ,利用该配置类进行判断属性源的优先级。

private void insertPropertySources(MutablePropertySources propertySources,
 CompositePropertySource composite) {
 MutablePropertySources incoming = new MutablePropertySources();
 incoming.addFirst(composite);
 PropertySourceBootstrapProperties remoteProperties = new PropertySourceBootstrapProperties();
 new RelaxedDataBinder(remoteProperties, "spring.cloud.config")
 .bind(new PropertySourcesPropertyValues(incoming));
 //如果不允许本地覆写
 if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone()
 && remoteProperties.isOverrideSystemProperties())) {
 propertySources.addFirst(composite);
 return;
 }
 //overrideNone为true,外部配置优先级最低
 if (remoteProperties.isOverrideNone()) {
 propertySources.addLast(composite);
 return;
 }
 if (propertySources
 .contains(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) {
 //根据overrideSystemProperties,设置外部配置的优先级
 if (!remoteProperties.isOverrideSystemProperties()) {
 propertySources.addAfter(
  StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
  composite);
 }
 else {
 propertySources.addBefore(
  StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
  composite);
 }
 }
 else {
 propertySources.addLast(composite);
 }
}

上述实现主要是根据 PropertySourceBootstrapProperties 中的属性,调整多个配置源的优先级。从其实现可以看到 PropertySourceBootstrapProperties 对象的是被直接初始化,使用的是默认的属性值而并未注入我们在配置文件中设置的。

修复后的实现:

@Autowired(required = false)
 private PropertySourceBootstrapProperties remotePropertiesForOverriding;
 @Override
 public int getOrder(){
 return this.order;
 private void insertPropertySources(MutablePropertySources propertySources,
 CompositePropertySource composite) {
 MutablePropertySources incoming = new MutablePropertySources();
 incoming.addFirst(composite);
 PropertySourceBootstrapProperties remoteProperties = remotePropertiesForOverriding == null
 ? new PropertySourceBootstrapProperties()
 : remotePropertiesForOverriding;

总结

以上所述是小编给大家介绍的Spring Cloud 覆写远端的配置属性实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

您可能感兴趣的文章:

  • spring cloud学习入门之config配置教程
  • spring cloud学习教程之config修改配置详解
  • spring cloud如何修复zuul跨域配置异常的问题
  • 详解Spring Cloud Zuul中路由配置细节
(0)

相关推荐

  • spring cloud如何修复zuul跨域配置异常的问题

    前言 本文主要给大家介绍一下在zuul进行跨域配置的时候出现异常该如何解决的方法,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 异常 The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed 实例 Access-Control-Allow-Credentials:true Access-Control-Allow-Credentials:t

  • spring cloud学习入门之config配置教程

    前言 本文主要给大家分享了关于spring cloud的入门教程,主要介绍了config配置的相关内容,下面话不多说了,来一起看看看详细的介绍吧. 简介 Spring cloud config 分为两部分 server client config-server 配置服务端,服务管理配置信息 config-client 客户端,客户端调用server端暴露接口获取配置信息 config-server 创建config-server 首先创建config-server工程. 文件结构: ├── co

  • spring cloud学习教程之config修改配置详解

    之前我们讲过了spring cloud之config配置的相关内容,那么在Git端修改配置后如何让客户端生效?下面来一起看看详细的介绍吧. 访问接口修改 refresh post方式执行http://localhost/refresh 会刷新env中的配置 restart 如果配置信息已经注入到bean中,由于bean是单例的,不会去加载修改后的配置 需要通过post方式去执行http://localhost/restart, 需要通过application.properties中配置endpo

  • 详解Spring Cloud Zuul中路由配置细节

    上篇文章我们介绍了API网关的基本构建方式以及请求过滤,小伙伴们对Zuul的作用应该已经有了一个基本的认识,但是对于路由的配置我们只是做了一个简单的介绍,本文我们就来看看路由配置的其他一些细节. 首先我们来回忆一下上篇文章我们配置路由规则的那两行代码: zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.serviceId=feign-consumer 我们说当我的访问地址符合/api-a/**规则的时候,会被自动定位到feign-consume

  • Spring Cloud 覆写远端的配置属性实例详解

    应用的配置源通常都是远端的Config Server服务器,默认情况下,本地的配置优先级低于远端配置仓库.如果想实现本地应用的系统变量和config文件覆盖远端仓库中的属性值,可以通过如下设置: spring: cloud: config: allowOverride: true overrideNone: true overrideSystemProperties: false overrideNone:当allowOverride为true时,overrideNone设置为true,外部的配

  • Spring Boot 2.0多数据源配置方法实例详解

    两个数据库实例,一个负责读,一个负责写. datasource-reader: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://192.168.43.61:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false username: icbc password: icbc driver-class-na

  • Spring框架构造注入type属性实例详解

    这篇文章主要介绍了Spring框架构造注入type属性实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 进行测试,验证一个问题,废话不多说了,上代码进行比较 package service.impl; import service.UserService; import dao.UserDao; import entity.User; /** * 用户业务类,实现对User功能的业务管理 */ public class UserServi

  • JS中的hasOwnProperty()和isPrototypeOf()属性实例详解

    这两个属性都是Object.prototype所提供:Object.prototype.hasOwnProperty()和Object.prototype.isPropertyOf() 先讲解hasOwnProperty()方法和使用.在讲解isPropertyOf()方法和使用 看懂这些至少要懂原型链 一.Object.prototype.hasOwnProperty() 概述 hasOwnProperty()方法用来判断某个对象是否含有指定的自身属性 语法 obj.hasOwnPropert

  • Spring jdbc中数据库操作对象化模型的实例详解

    Spring jdbc中数据库操作对象化模型的实例详解 Spring Jdbc数据库操作对象化 使用面向对象方式表示关系数据库的操作,实现一个线程安全可复用的对象模型,其顶级父类接口RdbmsOperation. SqlOperation继承该接口,实现数据库的select, update, call等操作. 1.查询接口:SqlQuery 1) GenericSqlQuery, UpdatableSqlQuery, MappingSqlQueryWithParameter 2) SqlUpda

  • Centos6 网络配置的实例详解

    Centos6 网络配置的实例详解 前言: 要实现永久的自定义IP或者更改DNS都需要修改配置文件,主要修改以下配置文件 /etc/sysconfig/network-scripts/ifcfg-ethX,其中ifcfg-ethX中的X代表第几块网卡,一般都是第一块,也就是ifcfg-eth0 下面是配置项目的讲解,这里展示的是自定义IP和DNS的配置文件 DEVICE=eth0#网卡设备名称 TYPE=Ethernet#网卡类型 UUID=06c04617-25d9-4a88-aab0-d8f

  • MYSQL8.0.13免安装版配置教程实例详解

    一.下载,本人以8.0为例 下载地址:https://dev.mysql.com/downloads/mysql/ 二.解压到某个目录,例如:D:/mysql/mysql-8.0.13-winx64 三.配置环境变量 1.新建一个变量:MYSQL_HOME 变量值:D:/mysql/mysql-8.0.13-winx64 2.修改path变量 添加一条记录:%MYSQL_HOME%/bin 四.在D:/mysql/mysql-8.0.13-winx64目录下创建my.ini文件 [mysqld]

  • Bootstrap的aria-label和aria-labelledby属性实例详解

    aria-label 正常情况下,form表单的input组件都有对应的label.当input组件获取到焦点时,屏幕阅读器会读出相应的label里的文本. <!DOCTYPE html> <html> <head> <meta charset = "utf-8"> <title>demo</title> <link rel="stylesheet" href="https://

  • spring boot udp或者tcp接收数据的实例详解

    下面用的是 springboot内置integration依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot<

  • Spring Cloud Config对特殊字符加密处理的方法详解

    前言 之前写过一篇关于配置中心对配置内容加密解密的介绍:<Spring Cloud构建微服务架构:分布式配置中心(加密解密) >.在这篇文章中,存在一个问题:当被加密内容包含一些诸如=.+这些特殊字符的时候,使用上篇文章中提到的类似这样的命令curl localhost:7001/encrypt -d去加密和解密的时候,会发现特殊字符丢失的情况. 比如下面这样的情况: $ curl localhost:7001/encrypt -d eF34+5edo= a34c76c4ddab706fbca

随机推荐