项目地址
springboot_01

我们在前面的文章应该看到了
load()、getPropertySources()、addLast()
三个方法。我们就来写一个简易的配置文件读取程序。

我们先看看,之前看到的这个最后的load()方法。

private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
				DocumentConsumer consumer) {
	Resource[] resources = getResources(location);
	for (Resource resource : resources) {
		try {
			if (resource == null || !resource.exists()) {
				if (this.logger.isTraceEnabled()) {
					StringBuilder description = getDescription("Skipped missing config ", location, resource,
							profile);
					this.logger.trace(description);
				}
				continue;
			}
			if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
				if (this.logger.isTraceEnabled()) {
					StringBuilder description = getDescription("Skipped empty config extension ", location,
							resource, profile);
					this.logger.trace(description);
				}
				continue;
			}
			String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
			List<Document> documents = loadDocuments(loader, name, resource);
			if (CollectionUtils.isEmpty(documents)) {
				if (this.logger.isTraceEnabled()) {
					StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
							profile);
					this.logger.trace(description);
				}
				continue;
			}
			List<Document> loaded = new ArrayList<>();
			for (Document document : documents) {
				if (filter.match(document)) {
					addActiveProfiles(document.getActiveProfiles());
					addIncludedProfiles(document.getIncludeProfiles());
					loaded.add(document);
				}
			}
			Collections.reverse(loaded);
			if (!loaded.isEmpty()) {
				loaded.forEach((document) -> consumer.accept(profile, document));
				if (this.logger.isDebugEnabled()) {
					StringBuilder description = getDescription("Loaded config file ", location, resource,
							profile);
					this.logger.debug(description);
				}
			}
		}
		catch (Exception ex) {
			StringBuilder description = getDescription("Failed to load property source from ", location,
					resource, profile);
			throw new IllegalStateException(description.toString(), ex);
		}
	}
}

这里面我们提到过,List<Document> documents = loadDocuments(loader, name, resource);这一行代码是读取配置文件,看看他的实现方法。

springboot 读取map配置文件 springboot读取配置文件源码_spring


可以从debug的信息看到documents的value为空。我们的配置文件是键值对,所以可能是在上面圈出来的那部分获取到的数据。

springboot 读取map配置文件 springboot读取配置文件源码_List_02


通过查看debug控制台,确实是在这个位置。

那么我们就查看这个方法吧。

springboot 读取map配置文件 springboot读取配置文件源码_spring_03


这两个类都很熟悉,只是针对不同的配置文件后缀,进入不同类。

springboot 读取map配置文件 springboot读取配置文件源码_java_04

springboot 读取map配置文件 springboot读取配置文件源码_List_05


springboot 读取map配置文件 springboot读取配置文件源码_List_06


进入this.process(callback, yaml, resource);

springboot 读取map配置文件 springboot读取配置文件源码_java_07


这里可能都了解。new UnicodeReader(resource.getInputStream());读取文件数据。

再回到

private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
				throws IOException {
	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;
}

这个方法中的documents = asDocuments(loaded);

springboot 读取map配置文件 springboot读取配置文件源码_java_08

那么基本步骤就了解了。

  1. 先读取配置文件信息。
  2. 包装properties
  3. getPropertySources()
  4. addLast()

好了,现在我们开始撸代码

创建my.properties文件。

name=龙小虬

我们之前提到过

springboot 读取map配置文件 springboot读取配置文件源码_List_09


这里扫描了spring.factories文件中的org.springframework.boot.SpringApplicationRunListener,那么我们也创建一个spring.factories文件。配置信息

springboot 读取map配置文件 springboot读取配置文件源码_java_10

org.springframework.boot.SpringApplicationRunListener=\
com.lxq.listener.MySpringApplicationRunListeners

并创建MySpringApplicationRunListeners.java

package com.lxq.listener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;

/**
 * @author 龙小虬
 * @date 2021/3/24 14:57
 */
public class MySpringApplicationRunListeners implements SpringApplicationRunListener {

    @Override
    public void starting() {
        System.out.println(">>>>>starting<<<<<");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        // 先读取配置文件到程序中,然后放入SpringBoot中
        Properties properties = new Properties();
        try {
            // 1. 读取我们的my.properties文件,配置编码,防止中文乱码
            properties.load(new InputStreamReader(this.getClass().getClassLoader().
                    getResourceAsStream("my.properties"), "GB2312"));
            // 2. 读取名称名称为my
            PropertySource propertySource = new PropertiesPropertySource("my", properties);
            //3. 将资源添加到项目中
            MutablePropertySources propertySources = environment.getPropertySources();
            //4. 通过api接口可以将配置文件读取 到SpringBoot项目中
            propertySources.addLast(propertySource);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

创建MyController.java

package com.lxq.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 龙小虬
 * @date 2021/3/24 14:53
 */
@RestController
public class MyController {

    @Value("${name}")
    private String name;

    @RequestMapping(value = "/test",produces = "text/plain;charset=UTF-8")
    public String test(){
        return "手动注入my.properties"+name;
    }
}

运行代码。直接报错

springboot 读取map配置文件 springboot读取配置文件源码_java_11

getDeclaredConstructor 很明显是反射执行错误。

那么我们看看哪里模仿出问题了。

springboot 读取map配置文件 springboot读取配置文件源码_spring_12


找到这个类。

springboot 读取map配置文件 springboot读取配置文件源码_java_13

可以看到这个类是有参构造,而我们是无参构造。所以我们也添加一个。其实还可以在另一个地方看到。我们提到过,starting()方法会被回调。所以我们看看他。

springboot 读取map配置文件 springboot读取配置文件源码_spring_14


再看到listener.starting()方法

springboot 读取map配置文件 springboot读取配置文件源码_java_15

这里是有参构造,所以,我们是真的这一步骤出问题了。

我们在MySpringApplicationRunListeners.java添加有参构造

private final SpringApplication application;

private final String[] args;

public MySpringApplicationRunListeners(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
}

运行,并访问http://localhost:8090/test

springboot 读取map配置文件 springboot读取配置文件源码_spring_16