熟悉Spring Boot的小伙伴想必都知道Spring Boot可以加载properties和yaml格式的配置文件,Spring Boot启动时会自动加载并解析其中的内容。
Properties配置文件简单示例
server.port=8080
Yaml配置文件简单示例
server:
port: 8080
但假设要加载json格式的配置文件?例如这样
{
"server": {
"port": 8080
}
}
这里介绍几个方法,测试的版本是Spring Boot 2.2.4,json解析框架是Fastjson。
方法1
类似于application.properties或application.yaml,该方法可以自动加载并解析application.json配置文件。
主要利用了Spring Boot的PropertySourceLoader接口和SPI机制,以下是接口定义
public interface PropertySourceLoader {
/**
* 返回支持的文件扩展名(不包括.)
* @return 文件扩展名
*/
String[] getFileExtensions();
/**
* 从资源中加载一个或者多个属性源
* @param name 每个属性源的前缀名
* @param resource 要加载的资源
* @return 属性源数组
* @throws IOException if the source cannot be loaded
*/
List<PropertySource<?>> load(String name, Resource resource) throws IOException;
}
我们可以看到已经官方已经有两个实现类:PropertiesPropertySourceLoader和YamlPropertySourceLoader,分别对应着properties配置文件加载类和yaml文件格式加载类。类似地,我们实现这两个方法。
public class JsonPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[]{"json"};
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
//从resource即application.json加载字符串
String jsonStr = readResourceAsString(resource);
//解析字符串为JSONObject
JSONObject jsonObject = (JSONObject) JSON.parse(jsonStr);
//map用于存放属性
Map<String,Object> map = new HashMap<>();
jsonObject.forEach((key, value) -> putEntry(map, key, value));
if (map.isEmpty()) {
return Collections.emptyList();
}
return Collections
.singletonList(new OriginTrackedMapPropertySource(name, Collections.unmodifiableMap(map), true));
}
/***
* 针对多级的结构
*/
private void putEntry(Map<String, Object> map, String key, Object value) {
if (value instanceof JSONObject){
((JSONObject) value).forEach((key1, value1) -> putEntry(map, key + "." + key1, value1));
}else if (value instanceof JSONArray){
map.put(key, convertArray((JSONArray)value));
} else{
map.put(key,value);
}
}
/**
* 你可以根据自身需要定义数组如何解析
* 将json数组转换为字符串 格式如下: 1,2,3
* @param array
* @return
*/
private String convertArray(JSONArray array) {
if (array.isEmpty()){
return "";
}
StringBuilder builder = new StringBuilder();
array.forEach(value -> {
builder.append(",");
if (value instanceof JSONObject || value instanceof JSONArray){
builder.append(JSON.toJSON(value));
}else {
builder.append(value);
}
});
return builder.substring(1);
}
/**
* 将资源加载为一个字符串
*/
private String readResourceAsString(Resource resource) throws IOException {
StringBuilder builder = new StringBuilder();
InputStream in = resource.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str ;
while ((str = bufferedReader.readLine())!=null){
builder.append(str);
}
//关闭流
bufferedReader.close();
inputStreamReader.close();
in.close();
return builder.toString();
}
}
然后利用Spring Boot的类SPI扩展机制,在META-INF/spring.factories加入
# 请替换等号后的内容为你自己项目中JsonPropertySourceLoader的全限定名
org.springframework.boot.env.PropertySourceLoader=com.example.demo.JsonPropertySourceLoader
方法2
方法1支持application.json配置文件的加载,但是如果想要加载其他文件名的文件(例如config.json),怎么办?
这里利用Spring Boot的动态管理配置文件扩展接口EnvironmentPostProcessor,为了文章的简洁,这里复用了方法1的JsonPropertySourceLoader的加载配置方法,但实际可以不要这个类。
public class JsonEnvironmentPostProcessor implements EnvironmentPostProcessor {
//存放自定义加载的配置文件列表,请根据自己需要修改
private String[] profiles= new String[]{"config.json"};
// 实际加载类
private PropertySourceLoader loader = new JsonPropertySourceLoader();
// 向environment注入配置
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
for (String profile : profiles){
Resource resource = new ClassPathResource(profile);
try {
PropertySource<?> propertySource = loader.load(profile, resource).get(0);
environment.getPropertySources().addLast(propertySource);
} catch (IOException e) {
// do nothing
}
}
}
}
在META-INF/spring.factories加入
# 请替换等号后的内容为你自己项目中JsonEnvironmentPostProcessor的全限定名
org.springframework.boot.env.EnvironmentPostProcessor=com.example.demo.JsonEnvironmentPostProcessor
注意的是,EnvironmentPostProcessor的加载顺序在PropertySourceLoader之前。
方法3
该方法可以支持@PropertySource注解,加载自定义配置文件。出于简洁,依然复用之前JsonPropertySourceLoader的方法。
实现如下
public class JsonPropertySourceFactory implements PropertySourceFactory {
private PropertySourceLoader loader = new JsonPropertySourceLoader();
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
return loader.load(name, resource.getResource()).get(0);
}
}
使用示例如下
@PropertySource(value = "myconfig.json",factory = JsonPropertySourceFactory.class)
除上述的方法外,还可以通过重写PropertiesLoaderSupport的loadProperties方法,需要注意加载顺序。