1.概述
如何通过Java配置和@PropertySource在Spring中设置和使用属性。
还将看到属性在Spring Boot中的工作方式。
2.通过注解注册属性文件
Spring 3.1还引入了新的@PropertySource注解,作为将属性源添加到环境的便捷机制。
可以将此注解与@Configuration注释结合使用:
@Configuration
@PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
//...
}
注册新属性文件的另一种非常有用的方法是使用占位符,它能够在运行时动态选择正确的文件:
@Configuration
@PropertySource({
"classpath:persistence-${envTarget:mysql}.properties"
})
@Data
public class PropertiesPlaceholderConfig {
@Value("${url}")
private String url;
}
application.properties配置如下
envTarget=mysql
persistence-mysql.properties配置如下
url=10.0.12.28
@RunWith(SpringRunner.class)
@SpringBootTest
public class PropertiesTest {
@Autowired
private PropertiesPlaceholderConfig propertiesPlaceholderConfig;
@Test
public void whenYamlFileProvidedThenInjectSimpleMap() {
Assert.assertEquals(propertiesPlaceholderConfig.getUrl(),"10.0.12.28");
}
}
2.1 定义多个属性文件位置
根据Java 8约定,@PropertySource批注是可重复的。 因此,如果我们使用的是Java 8或更高版本,则可以使用此批注定义多个属性位置:
@PropertySource("classpath:foo.properties")
@PropertySource("classpath:bar.properties")
public class PropertiesWithJavaConfig {
//...
}
也可以使用@PropertySources注解并指定@PropertySource的数组。 这可以在任何受支持的Java版本中使用,而不仅仅是Java 8或更高版本:
@PropertySources({
@PropertySource("classpath:foo.properties"),
@PropertySource("classpath:bar.properties")
})
public class PropertiesWithJavaConfig {
//...
}
无论哪种情况,值得注意的是,如果发生属性名称冲突,则最后读取的源优先。
3.使用/注入属性
注入带有@Value注解的属性很简单:
@Value( "${jdbc.url}" )
private String jdbcUrl;
还可以为该属性指定一个默认值:
@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;
在Spring 3.1中添加的新PropertySourcesPlaceholderConfigurer解决了bean定义属性值和@Value注解中的$ {…}占位符。
最后,可以使用Environment API获取属性的值:
@Autowired
private Environment env;
...
dataSource.setUrl(env.getProperty("jdbc.url"));
4.Spring Boot的属性
在进入属性的高级配置选项之前,花一些时间看一下Spring Boot中对新属性的支持。
一般而言,与标准Spring相比,此新支持涉及的配置更少,这当然是Spring Boot的主要目标之一。
4.1 application.properties:默认属性文件
引导程序将其典型约定优于配置方法应用于属性文件。 这意味着只需将application.properties文件放在src/ main/resources目录中,即可对其进行自动检测。 然后,可以照常注入任何已加载的属性。
因此,通过使用此默认文件,无需显式注册PropertySource甚至无需提供属性文件的路径。
如果需要,还可以使用环境属性在运行时配置其他文件:
java -jar app.jar --spring.config.location=classpath:/another-location.properties
从Spring Boot 2.3开始,还可以为配置文件指定通配符位置。
例如,可以将spring.config.location属性设置为config/*/:
java -jar app.jar --spring.config.location=config/*/
这样,Spring Boot将在jar文件之外寻找与config/*/目录模式匹配的配置文件。 当有多个配置属性源时,这会派上用场。
从版本2.4.0开始,Spring Boot支持使用多文档属性文件,这与YAML的设计类似:
baeldung.customProperty=defaultValue
#---
baeldung.customProperty=overriddenValue
请注意,对于属性文件,三点划线符号前面带有注释字符(#)。
4.2 特定于环境的属性文件
如果需要针对不同的环境,则Spring Boot中有一个内置的机制。
可以简单地在src/main/resources目录中定义一个application-environment.properties文件,然后使用相同的环境名称设置一个Spring配置文件。
例如,如果我们定义“dev”环境,则意味着必须先定义一个dev配置文件,然后再定义application-dev.properties。
该环境文件将被加载,并优先于默认属性文件。 请注意,默认文件仍将被加载,只是在发生属性冲突时,特定于环境的属性文件具有优先权。
4.3层次属性
如果将属性组合在一起,则可以使用@ConfigurationProperties注解,该注解会将这些属性层次结构映射到Java对象中。
来使用一些用于配置数据库连接的属性:
database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
然后使用注解将它们映射到数据库对象:
@ConfigurationProperties(prefix = "database")
@Data
public class Database {
String url;
String username;
String password;
}
Spring Boot再次将其约定应用在配置方法上,自动在属性名称及其对应字段之间进行映射。 需要提供的只是属性前缀。
4.4备选:YAML文件
Spring还支持YAML文件。
所有相同的命名规则都适用于特定于测试、特定于环境和默认属性文件。唯一的区别是文件扩展名和对类路径中的SnakeYAML库的依赖。
YAML特别适合分层属性存储。 以下属性文件:
database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
secret: foo
与以下YAML文件同义:
database:
url: jdbc:postgresql:/localhost:5432/instance
username: foo
password: bar
secret: foo
还值得一提的是,YAML文件不支持@PropertySource注解,因此,如果需要使用此注解,它将限制使用属性文件。
另一个引人注目的一点是,在2.4.0版中,Spring Boot更改了从多文档YAML文件加载属性的方式。 以前,添加它们的顺序是基于配置文件激活顺序的。 但是,在新版本中,该框架遵循与前面为.properties文件指示的排序规则相同的规则。 在文件中声明为较低的属性只会覆盖较高的属性。
此外,在此版本中,无法再从特定于配置文件的文档中激活配置文件,从而使结果更清晰,更可预测。
4.5导入其他配置文件
在2.4.0之前的版本中,Spring Boot允许使用spring.config.location和spring.config.additional-location属性包含其他配置文件,但是它们具有一定的局限性。 例如,必须在启动应用程序之前定义它们(作为环境或系统属性,或使用命令行参数),因为它们是在流程的早期使用的。
在提到的版本中,可以使用application.properties或application.yml文件中的spring.config.import属性轻松地包含其他文件。 此属性支持一些有趣的功能:
- 添加几个文件或目录
- 可以从类路径或从外部目录加载文件
- 指示如果找不到文件或者它是可选文件,启动过程是否应该失败
- 导入无扩展名文件
例子:
spring.config.import=classpath:additional-application.properties,
classpath:additional-application[.yml],
optional:file:./external.properties,
classpath:additional-application-properties/
注意:这里我们只是为了换行而使用换行符格式化了此属性。
4.6命令行参数中的属性
除了使用文件,还可以直接在命令行中传递属性:
java -jar app.jar --property="value"
还可以通过系统属性来执行此操作,这些属性在-jar命令之前而不是之后提供:
java -Dproperty.name="value" -jar app.jar
4.7环境变量的属性
Spring Boot还将检测环境变量,将其视为属性:
export name=value
java -jar app.jar
4.8属性值的随机化
如果不希望使用确定性属性值,则可以使用RandomValuePropertySource来随机化属性值:
random.number=${random.int}
random.long=${random.long}
random.uuid=${random.uuid}
5.使用原生Bean进行配置-PropertySourcesPlaceholderConfigurer
除了将属性放入Spring的便捷方法之外,我们还可以手动定义和注册属性配置bean。
使用PropertySourcesPlaceholderConfigurer可让我们完全控制配置,但缺点是过于冗长,并且在大多数情况下是不必要的。
使用Java配置定义这个bean:
@Bean
public static PropertySourcesPlaceholderConfigurer properties(){
PropertySourcesPlaceholderConfigurer pspc
= new PropertySourcesPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[ ]
{ new ClassPathResource( "foo.properties" ) };
pspc.setLocations( resources );
pspc.setIgnoreUnresolvablePlaceholders( true );
return pspc;
}
6.父子上下文中的属性
这个问题一次又一次地出现:当Web应用程序具有父上下文和子上下文时会发生什么? 父上下文可能具有一些通用的核心功能和bean,然后是一个(或多个)子上下文,可能包含特定于servlet的bean。
在那种情况下,定义属性文件并将其包含在这些上下文中的最佳方法是什么? 以及如何最好地从Spring检索这些属性?
给出一个简单的细分。
如果文件是在父上下文中定义的:
@Value在子上下文中有效:是
@Value在父级上下文中有效:是
子上下文中的environment.getPropert有效:是
父级上下文中的environment.getProperty有效:是
如果文件是在子上下文中定义的:
@Value在子上下文中有效:是
@Value在父级上下文中有效:否
子上下文中的environment.getProperty有效:是
父级上下文中的environment.getProperty有效:否