项目地址
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);
这一行代码是读取配置文件,看看他的实现方法。
可以从debug的信息看到documents的value为空。我们的配置文件是键值对,所以可能是在上面圈出来的那部分获取到的数据。
通过查看debug控制台,确实是在这个位置。
那么我们就查看这个方法吧。
这两个类都很熟悉,只是针对不同的配置文件后缀,进入不同类。
进入this.process(callback, yaml, resource);
这里可能都了解。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);
那么基本步骤就了解了。
- 先读取配置文件信息。
- 包装properties
- getPropertySources()
- addLast()
好了,现在我们开始撸代码
创建my.properties文件。
name=龙小虬
我们之前提到过
这里扫描了spring.factories文件中的org.springframework.boot.SpringApplicationRunListener
,那么我们也创建一个spring.factories文件。配置信息
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;
}
}
运行代码。直接报错
getDeclaredConstructor
很明显是反射执行错误。
那么我们看看哪里模仿出问题了。
找到这个类。
可以看到这个类是有参构造,而我们是无参构造。所以我们也添加一个。其实还可以在另一个地方看到。我们提到过,starting()方法会被回调。所以我们看看他。
再看到listener.starting()方法
这里是有参构造,所以,我们是真的这一步骤出问题了。
我们在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