前言
在项目启动时,需要读取一些业务上大量的配置文件,用来初始化数据或者配置设定值等,我们都知道使用SpringBoot的@ConfigurationProperties注解 + application.yml配置可以很方便的读取这些配置,并且映射成对象很方便的使用,但是这些配置文件比较多的时候,而且不希望与application.yml放在一起,那么用SpringBoot如何实现呢。
my-config.yml
my-config:
key1: hello
方法1 导入配置文件
application.yml中可以导入这些配置文件,然后继续使用@ConfigurationProperties注解,这样做的好处是最简单的,而且还可以增加my-conf-profile.yml的文件,以达到根据profile切换配置文件的目的
application.yml
spring:
config:
import: my-config.yml
MyConfig.java
@Data
@Configuration
@ConfigurationProperties(prefix="my-config")
public class MyConfig {
private String key1;
}
方法2 使用@PropertySource注解加载配置文件
MyConfig.java
@Data
@Configuration
@ConfigurationProperties(prefix="my-config")
@PropertySource(value = "classpath:my-config.yml", factory = YamlPropertySourceFactory.class)
public class MyConfig {
private String key1;
}
由于@PropertySource注解默认只支持properties格式的配置文件,所以要自定义yaml文件读取方式:
YamlPropertySourceFactory.java
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).get(0);
}
}
上面的方法中返回的PropertySource内部实际上是由一个展平的Map对象,并且无法修改,源码可参照OriginTrackedYamlLoader.java。如果要对读取后的内容再进一步编辑,可以使用YamlPropertiesFactoryBean读取成Properties的形式去操作:
YamlPropertySourceFactory.java
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(resource.getResource());
Properties properties = yamlPropertiesFactoryBean.getObject();
// 这里加入自定义的属性
properties.put("description", resource.getResource().getDescription());
return new PropertiesPropertySource(resource.getResource().getFilename(), properties);
}
}
以上的几个方法原理都是将属性文件读取到整个Spring容器中,然后通过 @ConfigurationProperties 注解使用一个对象去映射需要的属性而已,如果读取文件的内容存在相同的key,那么后面读取到的则会覆盖前面的。这时候可以通过手动绑定的方法将读取到的内容直接放到对象中。
方法3 使用YamlPropertySourceLoader + Binder进行手动绑定
以上方法都会将MyConfig对象注入到Spring容器中去,而且配置文件也会在上下文的环境中存在,如果不想放入容器或者想对绑定的过程做更定制化的处理(比如判断文件修改时间等),那么需要手动绑定,生成配置对象。
MyConfig.java
@Data
public class MyConfig {
private String key1;
}
BindDemo.java
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
public class BindDemo {
public MyConfig getMyConfig() throws IOException {
Resource resource = new ClassPathResource("my-config.yml");
// 查看文件最后修改时间
FileTime lastModifiedTime = Files.getLastModifiedTime(resource.getFile().toPath());
String format = lastModifiedTime.toInstant().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("my-config.yml last modified time: " + format);
List<PropertySource<?>> load = new YamlPropertySourceLoader().load(resource.getFilename(), resource);
Binder binder = new Binder(ConfigurationPropertySource.from(load.get(0)));
BindResult<DicProperties> bind = binder.bind("my-config", DicProperties.class); // 这里是配置的前缀,如果没有前缀,则传空字符串
return bind.get();
}
}
当然,以上也可以使用 YamlPropertiesFactoryBean 来完成:
BindDemo.java
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
public class BindDemo {
public MyConfig getMyConfig() throws IOException {
Resource resource = new ClassPathResource("my-config.yml");
// 查看文件最后修改时间
FileTime lastModifiedTime = Files.getLastModifiedTime(resource.getFile().toPath());
String format = lastModifiedTime.toInstant().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("my-config.yml last modified time: " + format);
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(resource);
Properties properties = yamlPropertiesFactoryBean.getObject();
// 这里加入自定义的属性
properties.put("description", resource.getResource().getDescription());
Binder binder = new Binder(ConfigurationPropertySource.from(new PropertiesPropertySource("my-config", properties)));
BindResult<DicProperties> bind = binder.bind("my-config", DicProperties.class); // 这里是配置的前缀,如果没有前缀,则传空字符串
return bind.get();
}
}