前言
这篇文章是接上一篇的,因为文章太长看起来太累,所以就分了一下
EnvironmentRepository.findOne 查找配置
上回说到 EnvironmentController 控制器 ,重点关注 labelled 方法, 我打了一个断点跟了一下,代码会走到了 EnvironmentEncryptorEnvironmentRepository#findOne方法中,源码如下:
@Override
public Environment findOne(String name, String profiles, String label) {
//调用了SearchPathCompositeEnvironmentRepository#findOne
Environment environment = this.delegate.findOne(name, profiles, label);
if (this.environmentEncryptor != null) {
//解密
environment = this.environmentEncryptor.decrypt(environment);
}
if (!this.overrides.isEmpty()) {
environment.addFirst(new PropertySource("overrides", this.overrides));
}
return environment;
}
通过断点看到,在EnvironmentEncryptorEnvironmentRepository#findOne
方法中,调用了SearchPathCompositeEnvironmentRepository#findOne
方法,SearchPathCompositeEnvironmentRepository
是一个可以通过SearchPathLocator
从文件系统中加载配置文件的EnvironmentRepository
。
而SearchPathCompositeEnvironmentRepository
并没有findOne方法,它使用的父类的方法CompositeEnvironmentRepository#findOne
,最终得到一个Environment 对象。然后通过 EnvironmentEncryptor
进行解密。
进行往下走流程,代码来到 CompositeEnvironmentRepository#findOne
public class CompositeEnvironmentRepository implements EnvironmentRepository {
//仓库列表
protected List<EnvironmentRepository> environmentRepositories;
@Override
public Environment findOne(String application, String profile, String label) {
//Environment 是对配置文件名,环境,分支等封装对象
Environment env = new Environment(application, new String[]{
profile}, label, null, null);
//是不是只配置了一个仓库
if(environmentRepositories.size() == 1) {
//这里调用的是MultipleJGitEnvironmentRepository#findOne
Environment envRepo = environmentRepositories.get(0).findOne(application, profile, label);
//把加载到的配置文件的内容设置给Environment
env.addAll(envRepo.getPropertySources());
//版本号
env.setVersion(envRepo.getVersion());
//状态
env.setState(envRepo.getState());
} else {
//如果配置了多个仓库,循环通过repo.findOne去下载配置
for (EnvironmentRepository repo : environmentRepositories) {
env.addAll(repo.findOne(application, profile, label).getPropertySources());
}
}
return env;
}
这里把配置文件名,环境名,分支名封装成Environment 对象,同时该对象也用来接收结果。然后判断了一下是否配置了多个仓库,多个仓库就循环调用findOne方法加载配置,我们这里只配置了一个仓库,走的是MultipleJGitEnvironmentRepository
的findOne方法,然后把结果设置给Environment 并返回Environment。
注意:env.addAll(envRepo.getPropertySources());
这行代码,PropertySources就是加载到的配置文件的内容了。
继续往后面走,代码来到 MultipleJGitEnvironmentRepository#findOne 方法中
@Override
public Environment findOne(String application, String profile, String label) {
...省略...
//这个getRepository方法中对url中的类似{application}的占位符做了一些替换
JGitEnvironmentRepository candidate = getRepository(this, application, profile,
label);
if (label == null) {
//如果label是null,默认使用master作为分支
label = candidate.getDefaultLabel();
}
if (candidate == this) {
//默认走这
return super.findOne(application, profile, label);
}
return candidate.findOne(application, profile, label);
}
该方法中对URL中的占位符如:{application},{label}等进行替换,然后判断如果label是空的,就使用master作为默认label。最后调用super.findOne方法,此时代码来到其父类 AbstractScmEnvironmentRepository#findOne 方法
@Override
public synchronized Environment findOne(String application, String profile, String label) {
//加载本机的环境存储库
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
new NativeEnvironmentProperties());
//加载Locations ,Locations是对本地配置文件的封装
Locations locations = getLocations(application, profile, label);
//locations.getLocations()得到本地配置文件路径
delegate.setSearchLocations(locations.getLocations());
//调用 NativeEnvironmentRepository #findOne方法加载配置
Environment result = delegate.findOne(application, profile, "");
//把版本号和label设置给result
result.setVersion(locations.getVersion());
result.setLabel(label);
//执行本地仓库的清理工作
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
getUri());
}
该方法做了这么几件事情:
- 把ConfigurableEnvironment交给一个NativeEnvironmentRepository对象,ConfigurableEnvironment中有当前配置中心微服务的原始配置。
- 调用 getLocations 方法得到Locations ,它封装了配置文件的 application,profile,label,version ,和本地存储远程拉取下来的配置文件的位置。如:file:/C:/Users/whale/AppData/Local/Temp/config-repo-8104345609176998816/
- 把配置文件的地址交给NativeEnvironmentRepository ,然后调用其findOne方法
- 最后执行clean清理
getLocations 加载本地配置
我们先看一下 getLocations 方法是怎么加载本地配置文件的,代码来到JGitEnvironmentRepository#getLocations
@Override
public synchronized Locations getLocations(String application, String profile,
String label) {
if (label == null) {
//默认使用master分支
label = this.defaultLabel;
}
//刷新配置,这个代码回去远程拉取最新的配置文件
String version = refresh(label);
//调用 getSearchLocations 处理一下本地配置文件地址,把结果封装成Locations
return new Locations(application, profile, label, version,
getSearchLocations(getWorkingDirectory(), application, profile, label));
}
这里调用了 refresh方法下载配置文件,然后调用 getSearchLocations 找到本地下载的配置文件地址,封装成Locations返回。
重点看一下 refresh方法,代码来到JGitEnvironmentRepository#refresh方法中
/**准备工作目录
* Get the working directory ready.
*/
public String refresh(String label) {
Git git = null;
try {
//【1】创建Git客户端
git = createGitClient();
//【2】判断是否要从远程拉取配置文件
if (shouldPull(git)) {
//【3】执行配置文件拉取动作
FetchResult fetchStatus = fetch(git, label);
if (deleteUntrackedBranches && fetchStatus != null) {
deleteUntrackedLocalBranches(fetchStatus.getTrackingRefUpdates(), git);
}
// checkout after fetch so we can get any new branches, tags, ect.
//【4】执行checkout操作,切到master分支
checkout(git, label);
//判断仓库的所有分支是否包含当前分支
if (isBranch(git, label)) {
// merge results from fetch
//【5】执行merge操作
merge(git, label);
if (!isClean(git, label)) {
logger.warn("The local repository is dirty or ahead of origin. Resetting"
+ " it to origin/" + label + ".");
resetHard(git, label, LOCAL_BRANCH_REF_PREFIX + label);
}
}
}
else {
// nothing to update so just checkout
checkout(git, label);
}
//【6】始终返回当前HEAD作为版本 , 把版本号返回
// always return what is currently HEAD as the version
return git.getRepository().findRef("HEAD").getObjectId().getName();
}
...省略...
}
//创建Git客户端
private Git createGitClient() throws IOException, GitAPIException {
//工作目录加锁
File lock = new File(getWorkingDirectory(), ".git/index.lock");
//锁是否存在
if (lock.exists()) {
// The only way this can happen is if another JVM (e.g. one that
// crashed earlier) created the lock. We can attempt to recover by
// wiping the slate clean.
logger.info("Deleting stale JGit lock file at " + lock);
//删除锁
lock.delete();
}
//工作中的配置文件是否创造
if (new File(getWorkingDirectory(), ".git").exists()) {
//打开Git仓库
return openGitRepository();
}
else {
//拷贝Git仓库
return copyRepository();
}
}
JGitEnvironmentRepository#refresh方法挺复杂的,简单点锁就是根据URL把Git仓库中的配置文件fetch到本地,然后进行checkout,merge等等。然后把本地配置文件的地址。
代码还要回到 .AbstractScmEnvironmentRepository#findOne ,刚才跟的是getLocations方法
@Override
public synchronized Environment findOne(String application, String profile, String label) {
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
new NativeEnvironmentProperties());
//查找配置的地址
Locations locations = getLocations(application, profile, label);
delegate.setSearchLocations(locations.getLocations());
//把locaitons变成 Environment对象
Environment result = delegate.findOne(application, profile, "");
result.setVersion(locations.getVersion());
result.setLabel(label);
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
getUri());
}
封装Environment
现在需要跟一下 delegate.findOne方法了,这里调用的是NativeEnvironmentRepository#findOne ,它主要是把配置封装成Environment 对象。
@Override
public Environment findOne(String config, String profile, String label) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(
PropertyPlaceholderAutoConfiguration.class);
//本机环境
ConfigurableEnvironment environment = getEnvironment(profile);
builder.environment(environment);
builder.web(WebApplicationType.NONE).bannerMode(Mode.OFF);
if (!logger.isDebugEnabled()) {
// Make the mini-application startup less verbose
builder.logStartupInfo(false);
}
//args中包括 application , profile , label, 配置文件地址
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 {
//clean方法会返回一个封装好配置项的Environment
return clean(new PassthruEnvironmentRepository(environment).findOne(config,
profile, label));
}
finally {
context.close();
}
}
文章结束,喜欢的话给个好评吧!!!