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有效:否