5-配置文件
Spring boot使用具有顺序的PropertySource加载配置,以便于配置属性的覆盖。
1,home目录下的devtools全局设置属性( ~/.spring-bootdevtools.properties ,如果使用devtools)。
2,测试用例上的@TestPropertySource注解
3,测试用例上的@SpringBootTest#properties注解
4,命令行参数
5,SPRING_APPLICATION_JSON 的属性(环境变量或系统属性中内嵌的内联
JSON)
6,ServletConfig 初始化参数
7,ServletContext 初始化参数
8,java:comp/env下的JNDI属性
9,System.getProperties()属性
10,操作系统环境变量
11,random.*当中RandomValuePropertySource属性
12,没有打进jar包的Profile-specific应用属性
13,打进jar包中的Profile-specific应用属性
14,没有打进jar包的应用配置( application.properties 和YAML变量)
15,打进jar包中的应用配置( application.properties 和YAML变量)
16,@Configuration 类上的 @PropertySource 注解
17,默认属性(使用 SpringApplication.setDefaultProperties 指定) 。
全局devTool配置
在home目录下添加一个.spring-boot-devtools.properties文件,该文件中的内容会作用于该设备上所有spring boot应用。Spring boot应用启动时会到设备的home目录查找这个文件,如果这个文件存在,则将文件中的内容设置到ConfigurableEnvironment对象当中。
devTool 属性针对Spring boot应用重启、以什么方式重启、加载那些类路径下的文件及视图缓存和session持久化等属性的配置。
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
File home = getHomeFolder();
File propertyFile = (home != null) ? new File(home, FILE_NAME) : null;
if (propertyFile != null && propertyFile.exists() && propertyFile.isFile()) {
FileSystemResource resource = new FileSystemResource(propertyFile);
Properties properties;
try {
properties = PropertiesLoaderUtils.loadProperties(resource);
environment.getPropertySources().addFirst(
new PropertiesPropertySource("devtools-local", properties));
}
catch (IOException ex) {
throw new IllegalStateException("Unable to load " + FILE_NAME, ex);
}
}
}
默认配置如下
devToolsProperties.put("spring.thymeleaf.cache", "false");
devToolsProperties.put("spring.freemarker.cache", "false");
devToolsProperties.put("spring.groovy.template.cache", "false");
devToolsProperties.put("spring.mustache.cache", "false");
devToolsProperties.put("server.servlet.session.persistent", "true");
devToolsProperties.put("spring.h2.console.enabled", "true");
devToolsProperties.put("spring.resources.cache.period", "0");
devToolsProperties.put("spring.resources.chain.cache", "false");
devToolsProperties.put("spring.template.provider.cache", "false");
devToolsProperties.put("spring.mvc.log-resolved-exception", "true");
devToolsProperties.put("server.servlet.jsp.init-parameters.development", "true");
devToolsProperties.put("spring.reactor.stacktrace-mode.enabled", "true");
配置随机值
RandomValuePropertySource可以产生整数,long或则字符串。
在application.properties文件中添加如下随机值(也可用于配置其他属性值):
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.less=${random.int(10)}
my.range=${random.int[1024,65536]}
定义一个bean,为了能够看到bean在初始化时完成属性设置,让bean实现org.springframework.beans.factory.InitializingBean接口。
@Component
public class MyRandomValue implements InitializingBean{
@Value("${my.secret}")
private String secret;
@Value("${my.number}")
private String number;
@Value("${my.bignumber}")
private String bignumber;
@Value("${my.less}")
private String less;
@Value("${my.range}")
private String range;
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println(this.toString());
}
@Override
public String toString() {
return "MyRandomValue [secret=" + secret + ", number=" + number
+ ", bignumber=" + bignumber + ", less=" + less + ", range="
+ range + "]";
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getBignumber() {
return bignumber;
}
public void setBignumber(String bignumber) {
this.bignumber = bignumber;
}
public String getLess() {
return less;
}
public void setLess(String less) {
this.less = less;
}
public String getRange() {
return range;
}
public void setRange(String range) {
this.range = range;
}
}
启动程序,观察输出:
image.png
命令行参数
新建一个bean同样实现org.springframework.beans.factory.InitializingBean接口,并实现接口方法;为了能够访问命令行参数,在bean中添加一个org.springframework.boot.ApplicationArguments属性,该接口提供对原始String[]参数的访问,提供对option和non-option参数的访问。
@Component
public class MyCommandLineValue implements InitializingBean{
@Autowired
private ApplicationArguments arg;
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println(this.getClass().getName() +" has been created ");
System.out.println(arg.containsOption("debug"));
System.out.println(arg.getNonOptionArgs());
}
}
在启动配置中添加--debug logfile.txt,然后运行程序,可以看到在bean被成功创建之后的输出:
image.png
在默认情况下,SpringApplication会将所有命令行参数,即以--开头的参数,转化为一个property,并添加到Spring Environment当中,所以可以通过@Value注解使用单个值。如果想忽略命令行参数对属性值的影响,可以通过SpringApplication.setAddCommandLineProperties(false)禁用。
application.properties属性文件
SpringApplication从当前目录下的/config子目录,当前目录,classpath下的config目录,classpath根目录加载配置文件并添加到Spring Environment当中。如果使用spring.config.location指定文件的加载路径则会按照classpath:,classpath:/config,file:,;file:config/顺序,指定的路径会优先于所有默认路径。通过设置spring.config.name可以指定加载的文件名,但必须将spring.config.name和spring.config.location配置为系统属性或命令行参数,以便于程序启动。如果spring.config.location包含子目录,则应该以“/”结尾。
使用@ConfigurationProperties注解
@ConfigurationProperties在类上使用,设置prefix属性。
application.properties中添加如下配置
test.name=test
test.id=this is for test
test.value=success
创建MyProperties并实现org.springframework.beans.factory.InitializingBean接口。
@Component
@ConfigurationProperties(prefix="test")
public class MyProperties implements InitializingBean{
private String name;
private String id;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "MyProperties [name=" + name + ", id=" + id + ", value=" + value
+ "]";
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println(this.getClass().getName()+" bean has bean created "+this.toString());
}
}
启动程序,观察类的属性值是否被成功映射。
image.png
Spring boot 将Environment属性绑定到@configurationProperties注解的bean时并不要求属性完全匹配,以下列举都可以实现匹配。
image.png
其次属性在匹配时会尝试进行属性的校验和类型转换。
属性校验
使用外部属性校验JSR-303 javax.validation;使用@Valid出发校验。
例如:
@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionProperties{
@NotNull
@Valid
private RemoteAddress remoteAddress;
public RemoteAddress getRemoteAddress() {
return remoteAddress;
}
public void setRemoteAddress(RemoteAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
public static class RemoteAddress{
@NotNull(message="url must be exist")
private String url;
@NotEmpty
private int port;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Override
public String toString() {
return "RemoteAddress [url=" + url + ", port=" + port + "]";
}
}
@Override
public String toString() {
return "ConnectionProperties [remoteAddress=" + remoteAddress + "]";
}
}
使用org.springframework.validation.Validator接口实现属性值的校验。
public class SamplePropertiesValidator implements Validator{
@Override
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return arg0 == ConnectionProperties.class;
}
@Override
public void validate(Object arg0, Errors arg1) {
// TODO Auto-generated method stub
ValidationUtils.rejectIfEmpty(arg1, "url", "url.empty");
ValidationUtils.rejectIfEmpty(arg1, "port", "port.empty");
ConnectionProperties properties = (ConnectionProperties) arg0;
if (properties.getRemoteAddress().getUrl() != null
&& !properties.getRemoteAddress().getUrl().equals("")) {
arg1.rejectValue("url", "Invalid url");
}
}
}
在@SpringBootApplication注解的类上中添加Validator bean
@Bean
public Validator configurationPropertiesValidator(){
return new SamplePropertiesValidator();
}
运行Spring boot程序,观察结果:
image.png
使用@PropertySource注解
property Source注解同样提供将配置文件添加到Spring Environment的功能。
在classpath下添加一个属性配置文件,例如app.properties,内容如下:
app.name=spring boot
app.author=zhongzhong
app.detail=test
创建AppConfig类并实现InitializingBean用来查看创建情况。
@Configuration
@PropertySource("classpath:app.properties")
public class AppConfig implements InitializingBean{
@Autowired
Environment environment;
@Bean
public App app(){
App app = new App();
app.setName(environment.getProperty("app.name"));
app.setDetail(environment.getProperty("app.detail"));
app.setAuthor(environment.getProperty("app.author"));
System.out.println("app is created");
return app;
}
private static class App{
private String name;
private String author;
private String detail;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
@Override
public String toString() {
return "App [name=" + name + ", author=" + author + ", detail="
+ detail + "]";
}
}
@Override
public String toString() {
return "AppConfig [app()=" + app() + "]";
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println(this.getClass().getName() + " "+this.toString());
}
}
运行程序:
image.png
@propertySource在指定配置文件路径时可以使用占位符,例如:
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
:default 为可选项。使用前提是my.placeholder已经在其他配置文件中定义,如果没有则使用默认路径/path,如果没有默认路径并且my.plcaeholder也不存在,则会抛出IllegalArgumentException。
如果程序当中有多个配置文件,且文件中key值可能重复需要考虑属性覆盖的问题,根据在Application Context中注册的先后顺序,先注册的配置类中的属性会被后注册的配置类中的属性覆盖。
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext();
ctx.register(ConfigA.class);
ctx.register(ConfigB.class);
ctx.refresh();