前话

根据前文得知,bootstrapContext引入了PropertySourceLocator接口供外部源加载配置,但作用是应用于子级ApplicationContext的环境变量Environment上,并不做更新维护操作。

具体的加载与维护更新外部源的配置信息,还是得有ConfigServer来完成,这也是本文分析的重点。

监听器

在这之前,笔者先查看此板块关联的监听器ConfigServerBootstrapApplicationListener,因为其比前文分析的BootstrapApplicationListener监听器优先级还高,不过内部代码很简单,笔者直接查看其复写的方法

    @Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
if (!environment.resolvePlaceholders("${spring.cloud.config.enabled:false}")
.equalsIgnoreCase("true")) {
if (!environment.getPropertySources().contains(this.propertySource.getName())) {
environment.getPropertySources().addLast(this.propertySource);
}
}
}

代码意思很简单,针对环境变量中spring.cloud.config.enabled属性如果值不为true则设置为false。根据官方上的代码注释来看,是用于屏蔽HTTP方式的访问,默认是开启屏蔽功能的。也就是屏蔽了ConfigServer暴露Restful方式的接口访问,其中该属性可通过System系统变量或者SpringApplicationBuilder类来进行设置,具体读者可查阅其官方注释

这个影响小,我们直接去查看其如何去加载外部源的

BootstrapContext关联类

优先分析与bootstrapContext相关的类,通过查看其板块下的spring.factories文件对应的BootstrapConfiguration键值

# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.config.server.bootstrap.ConfigServerBootstrapConfiguration,\
org.springframework.cloud.config.server.config.EncryptionAutoConfiguration

笔者挑选ConfigServerBootstrapConfiguration类作为主要的分析源头,内部的源码比较简单,笔者则全部放出来

// 系统变量或者bootstrap.properties文件指定了spring.cloud.config.server.bootstrap属性则生效
@Configuration
@ConditionalOnProperty("spring.cloud.config.server.bootstrap")
public class ConfigServerBootstrapConfiguration { @EnableConfigurationProperties(ConfigServerProperties.class)
@Import({ EnvironmentRepositoryConfiguration.class })
protected static class LocalPropertySourceLocatorConfiguration { @Autowired
private EnvironmentRepository repository; @Autowired
private ConfigClientProperties client; @Autowired
private ConfigServerProperties server; // 加载外部源入口
@Bean
public EnvironmentRepositoryPropertySourceLocator environmentRepositoryPropertySourceLocator() {
return new EnvironmentRepositoryPropertySourceLocator(this.repository, this.client.getName(),
this.client.getProfile(), getDefaultLabel());
} private String getDefaultLabel() {
if (StringUtils.hasText(this.client.getLabel())) {
return this.client.getLabel();
} else if (StringUtils.hasText(this.server.getDefaultLabel())) {
return this.server.getDefaultLabel();
}
return null;
} } }

根据当前环境下是否存在spring.cloud.config.server.bootstrap属性来决定是否通过Git/SVN/Vault等方式(下文将提及)加载外部源至子级的ConfigurableEnvironment对象中,默认不开启,需要用户配置。

具体通过什么方式获取外部资源则交由EnvironmentRepository接口去实现,我们先看下此接口的方法

public interface EnvironmentRepository {

    // 内部就一个方法,通过参数指定找寻对应的环境对象
Environment findOne(String application, String profile, String label); }

看来其支持多仓库源的配置,但这里注意一下此处的Environment回参是springcloud config client板块中的类,应该是对我们常见的环境变量作些过滤的作用。

EnvironmentRepositoryConfiguration

除了上述的方式引入此多环境仓库的配置类,ConfigServer对应的ConfigServerAutoConfiguration默认也会引入。废话少说,首先看下头部

@Configuration
@EnableConfigurationProperties({ SvnKitEnvironmentProperties.class, CredhubEnvironmentProperties.class,
JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class })
@Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultRepositoryConfiguration.class,
CredhubConfiguration.class, CredhubRepositoryConfiguration.class, SvnRepositoryConfiguration.class,
NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class, DefaultRepositoryConfiguration.class })
public class EnvironmentRepositoryConfiguration {
}

嗯,看起来很多,其实也就是针对不同源的资源进行相应的配置,比如常见的SVN/Jdbc/Git/Vault等方式。针对不同源,springcloud允许用户配置spring.profile.active属性来选择相应的源,即使不指定,springcloud也默认以Git方式获取仓库。本文以springcloud默认支持的Git方式作为分析的入口

GitRepositoryConfiguration

git方式的资源获取是通过配置GitRepositoryConfiguration类来实现的,笔者看下其代码

@Configuration
@Profile("git")
class GitRepositoryConfiguration extends DefaultRepositoryConfiguration {
}

直接去观察其继承的DefaultRepositoryConfiguration类,内部源码也很简单,顺便把其关联的一些bean也一同放上来,方便我们更清楚的了解

    // 多Git环境仓库属性配置,以spring.cloud.config.server.git作为开头
@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public MultipleJGitEnvironmentProperties multipleJGitEnvironmentProperties() {
return new MultipleJGitEnvironmentProperties();
} @Configuration
@ConditionalOnClass(TransportConfigCallback.class)
static class JGitFactoryConfig { // 多Git环境仓库的工厂类
@Bean
public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory(
ConfigurableEnvironment environment, ConfigServerProperties server,
Optional<ConfigurableHttpConnectionFactory> jgitHttpConnectionFactory,
Optional<TransportConfigCallback> customTransportConfigCallback) {
return new MultipleJGitEnvironmentRepositoryFactory(environment, server, jgitHttpConnectionFactory,
customTransportConfigCallback);
}
} @Configuration
@ConditionalOnClass({ HttpClient.class, TransportConfigCallback.class })
static class JGitHttpClientConfig { // HTTP连接工厂类
@Bean
public ConfigurableHttpConnectionFactory httpClientConnectionFactory() {
return new HttpClientConfigurableHttpConnectionFactory();
}
} @Configuration
@ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT)
class DefaultRepositoryConfiguration {
....
.... @Bean
public MultipleJGitEnvironmentRepository defaultEnvironmentRepository(
MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory,
MultipleJGitEnvironmentProperties environmentProperties) throws Exception {
return gitEnvironmentRepositoryFactory.build(environmentProperties);
}
}

这里注册的MultipleJGitEnvironmentRepository对象便是EnvironmentRepository接口的实现类,由其统一管理多Git仓库的资源。在分析此类之前,先对上述的代码作下分步骤的分析以免产生糊涂


1.多Git仓库属性配置MultipleJGitEnvironmentProperties,也就是配置Git仓库的地址以及访问方式等等。挑选比较重要的属性用于归纳(多仓库应用)

假设远程仓库地址为git@github.com:jtjsir/config_demo.git

spring.cloud.config.server.git.repos.A1.pattern=config*     #A1仓库的匹配规则(匹配源{application}/{profile}),默认为下一点的name
spring.cloud.config.server.git.repos.A1.name=config_demo #A1仓库的别名
spring.cloud.config.server.git.repos.A1.uri=git@github.com:jtjsir/config_demo.git #远程git仓库地址
spring.cloud.config.server.git.repos.A1.username=nancoasky@gmail.com #git帐号
spring.cloud.config.server.git.repos.A1.password=nanco123 #git密码
spring.cloud.config.server.git.repos.A1.passphrase= #ssh密码短语,默认为空
spring.cloud.config.server.git.repos.A1.basedir=/data/demo/cloud #本地保存路径
spring.cloud.config.server.git.repos.A1.defaultLabel=master #标签,类似git的分支概念

具体的用户可查看MultipleJGitEnvironmentProperties类去详细的查看各个属性的含义,同时也可以了解SSH方式的校验


2.Http连接工厂类HttpClientConfigurableHttpConnectionFactory,主要是支持http/https的Git访问方式。具体就不讲解了,读者可自行分析

MultipleJGitEnvironmentRepository

顾名思义,其实就是JGitEnvironmentRepository类的集合类,我们只需要关注其复写的findOne()方法,附上真正去查找相应配置资源的AbstractScmEnvironmentRepository#findOne()方法

    @Override
public synchronized Environment findOne(String application, String profile, String label) {
// 通过native方式去加载,也就是读取远程Git仓库的本地copy
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
new NativeEnvironmentProperties());
// 1.获取本地git仓库的查找路径
Locations locations = getLocations(application, profile, label);
delegate.setSearchLocations(locations.getLocations());
// 2.获取属性集合
Environment result = delegate.findOne(application, profile, "");
result.setVersion(locations.getVersion());
result.setLabel(label);
// 过滤下
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
getUri());
}

笔者分析上述标注的两点,分步骤来


1.获取本地git仓库的查找路径,对应的是JGitEnvironmentRepository#getLocations()方法

    @Override
public synchronized Locations getLocations(String application, String profile,
String label) {
// label代表git仓库的分支,默认为master
if (label == null) {
label = this.defaultLabel;
}
// 刷新本地git仓库,蕴含了拉取远程仓库、更新的操作。使用到了uri属性
String version = refresh(label);
// 使用到了basedir和searchPaths属性
return new Locations(application, profile, label, version,
getSearchLocations(getWorkingDirectory(), application, profile, label));
}

上述的搜寻路径格式如{basedir}/{searchPaths:/}。其中searchPaths的组合方式是{application}、{profile}、{label}的随意拼装,有很大的灵活性。比如

{basedir}/{application}/{profile}/{label:master}/
{basedir}/{application}-{profile}/{label:master}/
{basedir}/{label:master}/{application}/{profile}/ {basedir}/config_demo

其中{application}、{profile}、{label}属性都是非必须的。

备注
如果用户有多层目录的要求,则只需要通过(_)来代替"/"即可。
比如searchPaths={application},如果有二级目录则使用application(_)profile即可


2.获取属性集合,具体的如何去解析获取相应的配置信息且看NativeEnvironmentRepository#findOne()方法

    // 此时的label为空字符串
@Override
public Environment findOne(String config, String profile, String label) {
// 专门解析${}符号
SpringApplicationBuilder builder = new SpringApplicationBuilder(
PropertyPlaceholderAutoConfiguration.class);
// 设置spring.profiles.active=profile
ConfigurableEnvironment environment = getEnvironment(profile);
builder.environment(environment);
builder.web(WebApplicationType.NONE).bannerMode(Mode.OFF);
/** 设置spring.config.name=config,application
** 设置spring.config.location={basedir}/{searchPaths:/}
**
*/
String[] args = getArgs(config, profile, label);
// Explicitly set the listeners (to exclude logging listener which would change
// log levels in the caller)
builder.application()
.setListeners(Arrays.asList(new ConfigFileApplicationListener()));
ConfigurableApplicationContext context = builder.run(args);
environment.getPropertySources().remove("profiles");
try {
// 过滤系统内部的通用变量并缩减source对应的key
return clean(new PassthruEnvironmentRepository(environment).findOne(config,
profile, label));
}
finally {
context.close();
}
}

其实很简单就是跟我们平常springboot启动时一样,读取相应的配置文件(此处只支持yml、properties、yaml方式),读取的格式例子如下

{basedir}/{searchPaths:/}application.properties
{basedir}/{searchPaths:/}application.yml
{basedir}/{searchPaths:/}{application}.properties
{basedir}/{searchPaths:/}{application}.yml
{basedir}/{searchPaths:/}{application}-{profile}.yml
{basedir}/{searchPaths:/}{application}-{profile}.properties
---
{basedir}/{application}/{profile}/{label:master}/application.[properties|yml]
{basedir}/{application}-{profile}/{label:master}/{application}.[properties|yml]
{basedir}/{label:master}/{application}/{profile}/{application}-{profile}.[properties|yml]
---
{basedir}/config_demo/{application}-{profile}.[properties|yml]
{basedir}/config_demo/application.[properties|yml]

其中{application}、{profile}、{label}属性都是非必须的。

springcloud ConfigServer的工作原理的更多相关文章

  1. SpringCloud | 通过电商业务场景让你彻底明白SpringCloud核心组件的底层原理

    本文分为两个部分: Spring Cloud"全家桶"简单介绍. 通过实际电商业务场景,让你彻底明白Spring Cloud几个核心组件的底层原理. Spring Cloud介绍 ...

  2. 菜鸟学Struts2——Struts工作原理

    在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...

  3. 【夯实Nginx基础】Nginx工作原理和优化、漏洞

    本文地址 原文地址 本文提纲: 1.  Nginx的模块与工作原理    2.  Nginx的进程模型    3 . NginxFastCGI运行原理        3.1 什么是 FastCGI   ...

  4. HashMap的工作原理

    HashMap的工作原理   HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和HashMap之间 ...

  5. 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)

    RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...

  6. ThreadLocal 工作原理、部分源码分析

    1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...

  7. Servlet的生命周期及工作原理

    Servlet生命周期分为三个阶段: 1,初始化阶段  调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...

  8. 代码管理工具 --- git的学习笔记二《git的工作原理》

    通过几个问题来学习代码管理工具之git 一.git是什么?为什么要用它?使用它的好处?它与svn的区别,在Mac上,比较好用的git图形界面客户端有 git 是分布式的代码管理工具,使用它是因为,它便 ...

  9. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

随机推荐

  1. Restful Service 中 DateTime 在 url 中传递

    在C# url 中一旦包特殊字符,请求可能就无法送达.可以使用如下方法,最为便捷. 请求端: beginTime.Value.ToString("yyyyMMddHHmmss") ...

  2. java_线程

    线程1    与线程相关的概念    线程与进程的区别    线程创建策略    线程组        线程创建策略        并发应用中一般有两种不同的线程创建策略        1直接控制线程 ...

  3. Python小练习(二)

    按照下面的要求实现对列表的操作:       1)产生一个列表,其中有40个元素,每个元素是0到100的一个随机整数       2)如果这个列表中的数据代表着某个班级40人的分数,请计算成绩低于平均 ...

  4. JAVA常用工具类异常处理

    1异常的定义 异常就是与我们编译相违背在过程中出现的逻辑或忘记一些赋值等等 分为编译时错误和运行时错误 运行时异常 我们一般处理的时Exception异常: 异常处理 异常处理可以通过关键字try,c ...

  5. 【maven】之nexus常用的一些配置

    nexus私服主要是在项目和maven中央仓库中间做代理,一般在公司内网或者公司内部的一些私包,都需要这么个产品.下面主要是关于maven和nexus之间的一些配置 1.在pom中配置nexus私服 ...

  6. 如何将maven依赖项打进jar包,将一个完整的项目打进jar包

    目的:我的目的就是将项目所有的文件,包括pom文件中依赖的jar包都打进一个jar包里面. 过程:将下面的内容放到pom文件里面. </dependencies> <build> ...

  7. centos 设置中文环境

    方法1: [hl@localhost ~]$ LANG=zh_CN.UTF-8 #只对当前shell有效,临时设置 [hl@localhost ~]$ ll 总用量 drwxrwxr-x. hl hl ...

  8. rest api方式实现对文档库的管理

    写在前面 刚入职一家新公司,在对接app的时候需要获取到某公司的sharepoint上面的文档库,获取文档库列表,团队文档库中的文件和文件夹列表,个人文档库中的文件文件夹列表,及在app端进入文件夹的 ...

  9. windows下安装Kettle

    先下载kettle稳定版本https://community.hitachivantara.com/docs/DOC-1009855 下载后并且解压 安装kettle必须安装jdk在你的电脑上,ket ...

  10. Vue 目录结构 绑定数据 绑定属性 循环渲染数据

    一.目录结构分析 node_modules 项目所需要的各种依赖 src 开发用的资源 assets 静态资源文件 App.vue 根组件 main.js 配置路由时会用 .babelrc 配置文件 ...