返回介绍

Spring 系列

MyBatis

Netty

Dubbo

Tomcat

Redis

Nacos

Sentinel

RocketMQ

番外篇(JDK 1.8)

学习心得

SpringBoot 配置加载解析

发布于 2024-05-19 21:34:34 字数 12292 浏览 0 评论 0 收藏 0

Spring Boot application 文件加载

如何找到这个加载的过程

  1. 创建配置文件application.yml

  2. 全局搜索 yml

    image-20200319083048849

  3. 换成properties搜索

    image-20200319083140225

  4. 我们以yml为例打上断点开始源码追踪

看到调用堆栈

image-20200319083345067

  • 一步一步回上去看如何调用具体方法的

ConfigFileApplicationListener

  • 配置文件监听器

调用过程

image-20200319082131146

image-20200319082544653

org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        RandomValuePropertySource.addToEnvironment(environment);
        // 加载器加载信息
        new Loader(environment, resourceLoader).load();
    }

Loader

  • 配置资源加载器

构造方法

        Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            // 环境配置
            this.environment = environment;
            // 占位符处理器
            this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
            // 资源加载器
            this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
            // 配置信息加载器初始化
            this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
                    getClass().getClassLoader());
        }
  • 熟悉的老朋友this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()), 看看spring.factories有什么

    • 搜索目标: org.springframework.boot.env.PropertySourceLoader

      image-20200319084141748

image-20200319084151997

观察发现里面有一个YamlPropertySourceLoader和我们之前找 yml 字符串的时候找到的类是一样的。说明搜索方式没有什么问题。

image-20200319084357652

初始化完成,后续进行解析了

load 方法

        void load() {
            FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
                    (defaultProperties) -> {
                        this.profiles = new LinkedList<>();
                        this.processedProfiles = new LinkedList<>();
                        this.activatedProfiles = false;
                        this.loaded = new LinkedHashMap<>();
                        // 初始化配置文件
                        initializeProfiles();
                        while (!this.profiles.isEmpty()) {
                            Profile profile = this.profiles.poll();
                            if (isDefaultProfile(profile)) {
                                addProfileToEnvironment(profile.getName());
                            }
                            load(profile, this::getPositiveProfileFilter,
                                    addToLoaded(MutablePropertySources::addLast, false));
                            this.processedProfiles.add(profile);
                        }
                        load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
                        addLoadedPropertySources();
                        applyActiveProfiles(defaultProperties);
                    });
        }

initializeProfiles

  • 初始化private Deque<Profile> profiles; 属性
  • image-20200319084902957

load

  • org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
            getSearchLocations().forEach(
                    // 本地路径
                    (location) -> {
                        // 是不是文件夹
                        boolean isFolder = location.endsWith("/");
                        // 文件名,默认application
                        Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
                        // 循环加载
                        names.forEach((name) -> {
                            load(location, name, profile, filterFactory, consumer);
                        });
                    });
        }
  • 资源路径可能性

image-20200319085446640

该方法采用循环每个路径下面都去尝试一遍

  • 中间过程省略,我们直接看最后的加载行为
    • org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#loadDocuments
        private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
                throws IOException {
            // 文档的缓存key
            DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
            // 文档信息
            List<Document> documents = this.loadDocumentsCache.get(cacheKey);
            if (documents == null) {
                // 执行加载,将配置文件读取返回
                List<PropertySource<?>> loaded = loader.load(name, resource);
                // 数据转换
                documents = asDocuments(loaded);
                // 缓存设置
                this.loadDocumentsCache.put(cacheKey, documents);
            }
            return documents;
        }

此处的loader.load()调用具体的 loader 实现类进行执行方法

yml 解析

    @Override
    public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
        if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
            throw new IllegalStateException(
                    "Attempted to load " + name + " but snakeyaml was not found on the classpath");
        }
        // 将资源转换成集合对象
        List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
        if (loaded.isEmpty()) {
            return Collections.emptyList();
        }
        List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
        for (int i = 0; i < loaded.size(); i++) {
            String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
            // 放入返回结果中
            propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
                    Collections.unmodifiableMap(loaded.get(i)), true));
        }
        return propertySources;
    }

image-20200319090446231

  • PropertiesPropertySourceLoader解析同理不在次展开描述了

asDocuments

        /**
         * 将 {@link PropertySource} 转换成 {@link Document}
         * @param loaded
         * @return
         */
        private List<Document> asDocuments(List<PropertySource<?>> loaded) {
            if (loaded == null) {
                return Collections.emptyList();
            }
            return loaded.stream().map(
                    // 循环创建新对象
                    (propertySource) -> {
                        // 对象创建
                        Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
                                this.placeholdersResolver);
                        /**
                         * 通过 {@link Binder} 将数据进行绑定,创建 {@link Document}进行返回
                         */
                        return new Document(propertySource, binder.bind("spring.profiles", STRING_ARRAY).orElse(null),
                                getProfiles(binder, ACTIVE_PROFILES_PROPERTY),
                                getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
                    }).collect(Collectors.toList());
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文