(一)基础知识学习
  参见​Spring Cloud 配置客户端技术基础

(二)客户单端配置
  主要涉及以下知识:

  • Bootstrap 配置文件
  • 调整 Bootstrap 配置文件路径
  • 覆盖远程配置文件属性
  • 自定义 Bootstrap 配置
  • 自定义 Bootstrap 配置属性源

1、Bootstrap 配置文件
  注意事项一:
  加载 Bootstrap 配置文件的监听器为:BootstrapApplicationListener

  注意事项二:

String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");

  这段代码的意思是,若未配置​​spring.cloud.bootstrap.name​​​属性,则读取​​bootstrap.properties​​​或​​bootstrap.yml​​​为Bootstrap 配置文件;若配置​​spring.cloud.bootstrap.name=test​​​,则读取​​test.properties​​​或​​test.yml​​为Bootstrap 配置文件。

  注意事三:

#application.properties
#通过调整 spring.cloud.bootstrap.enabled = false,尝试关闭 bootstrap 上下文
#实际测试结果,没有效果
spring.cloud.bootstrap.enabled = false

  原因解析:BootstrapApplicationListener 加载实际早于 ConfigFileApplicationListener(加载 application.yml 的监听器):

  • ConfigFileApplicationListener 的 Ordered.HIGHEST_PRECEDENCE +10(第十一位加载)
  • BootstrapApplicationListener 的 Ordered.HIGHEST_PRECEDENCE + 5(第六位加载)

  要想 ​​spring.cloud.bootstrap.enabled = false​​​ 生效,解决办法之一,就是在JVM的启动参数里设置​​--spring.cloud.bootstrap.enabled = false​​(这里的启动优先级比这两个更高)

  注意事项四:
  JVM启动参数的加载逻辑:

SpringApplication.configurePropertySources()

2、调整 Bootstrap 配置文件路径
  通过前面可知,BootstrapApplicationListener 加载实际早于 ConfigFileApplicationListener(加载 application.yml 的监听器),所以想在application.yml中调整 Bootstrap 配置文件路径是无法实现的,仍然需要在JVM的启动参数里设置,设置项为​​​--spring.cloud.bootstrap.location = config​​。调整 Bootstrap 配置文件路径之后,配置文件的加载顺序是什么,这里用一个Demo展示:

在不同的配置文件中设置 application 的名字:
application.yml
spring.application.name = spring-cloud-application
bootstrap.yml
spring.application.name = spring-cloud-bootstrap
spring-cloud.yml
spring.application.name = spring-cloud
config/spring-cloud.yml
spring.application.name = spring-cloud-config

设置JVM启动参数:
//设置 bootstrap 配置文件名为 spring-cloud
--spring.cloud.bootstrap.name = spring-cloud
//设置 Bootstrap 配置文件路径classpath:/config
--spring.cloud.bootstrap.location = config

加载效果:
applicationConfig: [classpath:/application.yml] : {
spring.application.name = spring-cloud-application
}
......
applicationConfig: [classpath:config/spring-cloud.yml] : {
spring.application.name = spring-cloud-application
}
applicationConfig: [classpath:/spring-cloud.yml] : {
spring.application.name = spring-cloud-application
}

备注:spring.application.name 最终读取的是 application.yml 中配置的项,而读取文件的顺序是:
先读 application.yml:不用说,Spring Boot上下文肯定会读到
再读:config/spring-cloud.yml:也不用说,JVM参数设置的bootstrap 配置文件名和路径
最后读spring-cloud.yml:这里就比较奇怪了,为什么还读 resources 路径下的spring-cloud.yml??

3、覆盖远程配置文件属性
  默认情况,Spring Cloud 是容许覆盖的:​​​spring.cloud.config.allowOverride=true​​ 。可以通过JVM启动参数,调整该属性为false(PS:但是这在本地无法生效,本地依然可以覆盖!!!这个仅适用于远程配置,远程无法再覆盖属性):

--spring.cloud.config.allowOverride=false

4、自定义 Bootstrap 配置
  (1)创建​​​META-INF/spring.factories​​​文件,类似于Spring Boot 自定义 Starter;
  (2)自定义Bootstrap 配置Configuration

package org.pc.bootstrap;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;

import java.util.HashMap;
import java.util.Map;

/**
*自定义 Bootstrap 配置 Bean
* @author 咸鱼
* @date 2018/10/30 19:22
*/
@Configuration
public class MyConfiguration implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
//从 ConfigurableApplicationContext 获取 ConfigurableEnvironment 实例
ConfigurableEnvironment environment = applicationContext.getEnvironment();
//获取 PropertySources
MutablePropertySources propertySources = environment.getPropertySources();
//定义一个新的 PropertySource
PropertySource propertySource = createPropertySource();
//将新建的 PropertySource 增加到集合中,并且放置在首位
propertySources.addFirst(propertySource);
}

private PropertySource createPropertySource() {
Map<String, Object> source = new HashMap<>();
source.put("name", "咸鱼");
PropertySource propertySource = new MapPropertySource("my-property-source", source);
return propertySource;
}
}

  (2)配置​​META-INF/spring.factories​​​文件,关联 Key ​​org.springframework.cloud.bootstrap.BootstrapConfiguration​

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.pc.bootstrap.MyConfiguration

  通过以上步骤,就可以将自定义的变量放到Bootstrap 上下文中,可以通过​​http://localhost:8081/actuator/env​​查看新增的属性:

{
"name": "my-property-source",
"properties": {
"name": {
"value": "咸鱼"
}
}
}

5、自定义 Bootstrap 配置属性源
  (1)自定义实现 PropertySourceLocator 接口

package org.pc.bootstrap;

import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.env.*;

import java.util.HashMap;
import java.util.Map;

/**
* 自定义 {@link PropertySourceLocator} 实现
* @author 咸鱼
* @date 2018/10/30 19:42
*/
public class MyPropertySourceLocator implements PropertySourceLocator {
@Override
public PropertySource<?> locate(Environment environment) {
//判断是否是 ConfigurableEnvironment 类型
if (environment instanceof ConfigurableEnvironment){
//若是,强转
ConfigurableEnvironment configurableEnvironment = ConfigurableEnvironment.class.cast(environment);
//获取 PropertySources
MutablePropertySources propertySources = configurableEnvironment.getPropertySources();
//将新建的 PropertySource 增加到集合中,并且放置在首位
propertySources.addFirst(createPropertySource());
}
return null;
}

private PropertySource createPropertySource() {
Map<String, Object> source = new HashMap<>();
source.put("nickName", "咸鱼昵称");
//设置名称 来源
PropertySource propertySource = new MapPropertySource("my-property-source-1", source);
return propertySource;
}
}

  (2)配置​​META-INF/spring.factories​​​文件,关联 Key ​​org.springframework.cloud.bootstrap.BootstrapConfiguration​

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.pc.bootstrap.MyPropertySourceLocator

  通过以上步骤,就可以将自定义的变量放到Bootstrap 上下文中,可以通过​​http://localhost:8081/actuator/env​​查看新增的属性:

{
"name": "my-property-source-1",
"properties": {
"nickName": {
"value": "咸鱼昵称"
}
}
}

(三)客户端和服务端联合配置
  参见​三、Spring cloud之服务器配置和客户端配置