二十四.SpringCloudConfig源码-配置拉取流程

简介: 这篇文章是接上一篇的,因为文章太长看起来太累,所以就分了一下

前言

这篇文章是接上一篇的,因为文章太长看起来太累,所以就分了一下

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());
}

该方法做了这么几件事情:

  1. 把ConfigurableEnvironment交给一个NativeEnvironmentRepository对象,ConfigurableEnvironment中有当前配置中心微服务的原始配置。
  2. 调用 getLocations 方法得到Locations ,它封装了配置文件的 application,profile,label,version ,和本地存储远程拉取下来的配置文件的位置。如:file:/C:/Users/whale/AppData/Local/Temp/config-repo-8104345609176998816/
  3. 把配置文件的地址交给NativeEnvironmentRepository ,然后调用其findOne方法
  4. 最后执行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();
        }
    }

文章结束,喜欢的话给个好评吧!!!

相关文章
|
7月前
|
自然语言处理 安全 API
MCP:让大语言模型不只是聊天,更能"动手做事"的开放协议
MCP(Model Context Protocol,模型上下文协议)是一种开放协议,旨在标准化应用程序向大语言模型提供上下文的方式。通过 MCP,大模型不仅能聊天,还能执行如查数据库、写代码等任务。相比 Function Calling,MCP 解耦了工具调用,提升灵活性和复用性,实现“一次开发,多处调用”。其架构包括 MCP Hosts、Clients、Servers,以及本地数据源和远程服务,支持安全访问多种资源。相关资源可在 GitHub 和 mcp.so 获取。
1201 3
MCP:让大语言模型不只是聊天,更能"动手做事"的开放协议
|
机器学习/深度学习 人工智能 自然语言处理
【人工智能】Transformers之Pipeline(三):文本转音频(text-to-audio/text-to-speech)
【人工智能】Transformers之Pipeline(三):文本转音频(text-to-audio/text-to-speech)
348 1
【人工智能】Transformers之Pipeline(三):文本转音频(text-to-audio/text-to-speech)
|
Linux 开发者
bpftrace使用案例学习
bpftrace使用案例学习
|
Java 索引 安全
[Mvel]Mvel2.0使用指南一 基础
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunnyYoona/article/details/75244442 MVEL在很大程度上受到Java语法的启发,作为一个表达式语言,也有一些根本的区别,旨在更高的效率,例如:直接支持集合、数组和字符串匹配等操作以及正则表达式。
16157 0
|
缓存 Java Ruby
程序与技术分享:Aviator表达式求值引擎开源框架
程序与技术分享:Aviator表达式求值引擎开源框架
550 0
|
开发者 Python
【Python】已解决:(Python3中pip无法安装urllib报错问题) ERROR: Could not find a version that satisfies the requireme
【Python】已解决:(Python3中pip无法安装urllib报错问题) ERROR: Could not find a version that satisfies the requireme
1259 0
【Python】已解决:(Python3中pip无法安装urllib报错问题) ERROR: Could not find a version that satisfies the requireme
|
存储 关系型数据库 MySQL
MySQL周内训参照1、ER实体关系图与数据库模型图绘制
MySQL周内训参照1、ER实体关系图与数据库模型图绘制
522 1
|
Java API Nacos
spring.config.import 是一个 Spring Cloud Config Server 的属性,
spring.config.import 是一个 Spring Cloud Config Server 的属性,【1月更文挑战第25天】【1月更文挑战第123篇】
2663 1
|
数据采集 人工智能 搜索推荐
【AI大模型应用开发】【综合实战】AI+搜索,手把手带你实现属于你的AI搜索引擎(附完整代码)
【AI大模型应用开发】【综合实战】AI+搜索,手把手带你实现属于你的AI搜索引擎(附完整代码)
909 0
|
JavaScript
查看NodeJs版本和查看NPM版本
查看NodeJs版本和查看NPM版本
688 0
查看NodeJs版本和查看NPM版本

热门文章

最新文章