Spring Boot 教程10:Properties

Spring Boot 教程10:Properties

声明

这个系列文章是翻译自https://www.baeldung.com/的系列博客,个人感觉是一个非常不错的Spring Boot 教程。原网站属于一个公司,主要开展有偿培训业务,但提供相关文字教程的免费阅读和下载。因为我并没有在网页找到相关版权描述信息,所以并不能确定是否可以自由翻译和转载,如果有版权问题,请联系我,我会撤下这个系列文章。

原文地址:Properties with Spring and Spring Boot

因为版权的关系,本文禁止转载。


概述

本教程将展示如何通过Java配置和 @PropertySource. 在Spring中设置和使用属性

我们还将看到属性如何在Spring Boot 中工作。

通过注释注册属性文件

Spring 3.1还引入了新的 @PropertySource 注释,作为将属性源添加到环境中的方便机制。

我们可以将此注释与 @Configuration 注释结合使用:

@Configuration
@PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
    //...
}

注册新属性文件的另一个非常有用的方法是使用占位符,它允许我们在运行时动态地选择正确的文件:

@PropertySource({ 
  "classpath:persistence-${envTarget:mysql}.properties"
})
...

定义多个属性位置

@PropertySource 注释根据Java 8约定是可重复的。因此,如果我们使用的是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 {
    //...
}

无论是哪种情况,都值得注意的是,在属性名冲突的情况下,最后一次源读取优先。

使用/注入属性

注入一个带有**@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"));

使用 Spring Boot 属性

在我们进入属性的更高级配置选项之前,让我们花一些时间来看看Spring Boot 中的新属性支持。

一般来说,与标准Spring相比,这种新的支持涉及的配置更少,这当然是Boot 的主要目标之一。

application.properties:默认属性文件

Boot 将其典型的约定优于配置方法应用于属性文件。这意味着我们可以简单地将 application.properties 文件放在src/main/resources 目录中,它将被自动检测。然后我们可以像往常一样从它注入任何加载的属性。

因此,通过使用这个默认文件,我们不必显式注册 PropertySource,甚至不必提供属性文件的路径。

如果需要,我们还可以在运行时配置不同的文件,使用环境属性:

java -jar app.jar --spring.config.location=classpath:/another-location.properties

从Spring Boot 2.3开始,我们还可以为配置文件指定通配符位置(location)。

例如,我们可以将 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

注意,对于属性文件,三个破折号符号前面有一个注释字符( # )。

环境特定属性文件

如果我们需要针对不同的环境,Boot 中有一个内置的机制。

我们可以简单地在src/main/resources目录中定义一个application-environment.properties文件,然后使用相同的环境名称(environment name)设置Spring配置(profile)。

例如,如果我们定义了一个“staging”环境,这意味着我们必须定义一个staging配置文件,然后定义application-staging. properties。

将加载此env文件,并优先于默认属性文件。请注意,默认文件仍然会被加载,只是当存在属性冲突时,环境特定的属性文件优先。

测试特定属性文件

我们可能还需要在测试应用程序时使用不同的属性值。

Spring Boot 通过在测试运行期间查看我们的 src/test/resources 目录来处理这个问题。同样,默认属性仍然可以正常注入,但如果发生冲突,这些属性将被覆盖。

@TestPropertySource 注解

如果我们需要对测试属性进行更细粒度的控制,那么我们可以使用 @TestPropertySource 注解。

这允许我们为特定的测试上下文设置测试属性,优先于默认的属性源:

@RunWith(SpringRunner.class)
@TestPropertySource("/foo.properties")
public class FilePropertyInjectionUnitTest {

    @Value("${foo}")
    private String foo;

    @Test
    public void whenFilePropertyProvided_thenProperlyInjected() {
        assertThat(foo).isEqualTo("bar");
    }
}

如果我们不想使用文件,我们可以直接指定名称和值:

@RunWith(SpringRunner.class)
@TestPropertySource(properties = {"foo=bar"})
public class PropertyInjectionUnitTest {

    @Value("${foo}")
    private String foo;

    @Test
    public void whenPropertyProvided_thenProperlyInjected() {
        assertThat(foo).isEqualTo("bar");
    }
}

我们也可以使用 @SpringBootTest 注解 的properties参数实现类似的效果:

@RunWith(SpringRunner.class)
@SpringBootTest(
  properties = {"foo=bar"}, classes = SpringBootPropertiesTestApplication.class)
public class SpringBootPropertyInjectionIntegrationTest {

    @Value("${foo}")
    private String foo;

    @Test
    public void whenSpringBootPropertyProvided_thenProperlyInjected() {
        assertThat(foo).isEqualTo("bar");
    }
}

分层属性

如果我们有分组在一起的属性,我们可以使用 @ConfigurationProperties 注释,它将把这些属性层次结构映射到Java对象图中。

让我们使用一些用于配置数据库连接的属性:

database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar

然后让我们使用注释将它们映射到数据库对象:

@ConfigurationProperties(prefix = "database")
public class Database {
    String url;
    String username;
    String password;

    // standard getters and setters
}

Spring Boot 再次将其约定应用于配置方法,自动在属性名称及其对应的字段之间进行映射。我们需要提供的只是属性前缀。

如果您想深入了解配置属性,请查看我们的深入文章

备选方案: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文件指出的相同排序规则;在文件中声明在较低位置的属性将简单地覆盖较高位置的属性。

此外,在这个版本中,配置文件不能再从特定于配置文件的文档中激活,使结果更清晰,更可预测。

导入其他配置文件

在2.4.0版本之前,Spring Boot 允许使用 spring.config.locationspring.config.additional-location 属性包含其他配置文件,但它们有一定的限制。例如,它们必须在启动应用程序之前定义(作为环境或系统属性,或使用命令行参数),因为它们在过程的早期使用。

在上述版本中,我们可以使用 application.propertiesapplication.yml 文件中的 spring.config.import 属性来轻松包含其他文件。此属性支持一些有趣的功能:

  • 添加多个文件或目录
  • 可以从类路径或外部目录加载文件
  • 指示如果未找到文件或文件是可选文件,启动过程是否应失败
  • 导入无扩展名文件

让我们看一个有效的例子:

spring.config.import=classpath:additional-application.properties,
  classpath:additional-application[.yml],
  optional:file:./external.properties,
  classpath:additional-application-properties/

注意:这里我们使用换行符来格式化这个属性,只是为了清楚起见。

Spring将把 import 视为直接插入到import声明下面的新文档。

命令行参数的属性

除了使用文件,我们还可以直接在命令行上传递属性:

java -jar app.jar --property="value"

我们也可以通过系统属性来实现这一点,这些属性在-jar命令之前而不是之后提供:

java -Dproperty.name="value" -jar app.jar

环境变量的属性

Spring boot 还将检测环境变量,将其视为属性:

export name=value
java -jar app.jar

属性值的随机化

如果我们不想要决定论属性值,我们可以使用 RandomValuePropertySource 来随机化属性值:

random.number=${random.int}
random.long=${random.long}
random.uuid=${random.uuid}

其他类型的属性源

Spring Boot 支持多种属性源,实现了深思熟虑的排序以允许明智的重写。参考官方文档是值得的,它超出了本文的范围。

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;
}

父子上下文中的属性

这个问题反复出现:当我们的Web应用程序有一个父上下文和一个子上下文时会发生什么?父上下文可能有一些公共的核心功能和bean,然后是一个(或多个)子上下文,可能包含特定于servlet的bean。

在这种情况下,定义属性文件并将其包含在这些上下文中的最佳方法是什么?如何从Spring中最好地检索这些属性?

我们将给予一个简单的分解。

如果文件是在父上下文中定义的:

  • @Value 适用于Child上下文:是的

  • @Value 在父上下文中工作:是的

  • 在Child上下文中的environment.getProperty:是的

  • environment.getProperty在父上下文中:是的

如果文件是在“子”上下文中定义的:

  • @Value 适用于Child上下文:是的

  • @Value 在父上下文中工作:否

  • 在Child上下文中的environment.getProperty:是的

  • environment.getProperty在父上下文中:否

结论

本文展示了在Spring中使用属性和属性文件的几个示例。

和往常一样,支持本文的整个代码都可以在GitHub上找到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值