spring boot配置文件加载顺序 (spring boot多数据源配置)

如何通过 Java 配置和@PropertySource在 Spring 中设置和使用属性。

springboot中全局设置和局部设置,springboot开发中配置类是什么

1. 通过注释注册属性文件

Spring 3.1 还引入了新的 @PropertySource 注释,作为将属性源添加到环境中的便捷机制。我们可以将此注释与 @Configuration 注释结合使用:

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

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

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

1.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 {
    //...
}

值得注意的是,无论哪种情况,如果发生属性名称冲突,最后一个源读取优先。

2. 使用/注入属性

使用 @Value 注释注入属性非常简单:

@Value( "${jdbc.url}" )
private String jdbcUrl;

我们还可以为属性指定默认值:

@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;

Spring 3.1 中添加的新 PropertySourcesPlaceholderConfigurer 解决了 bean 定义属性值和 @Value 注释中的 ${...} 占位符。

最后,我们可以使用 环境 API 获取属性的值:

@Autowired
private Environment env;
...
dataSource.setUrl(env.getProperty("jdbc.url"));

3. Spring Boot的属性

在我们进入属性的更高级配置选项之前,让我们花一些时间看一下 Spring Boot 中的新属性支持。一般来说,与标准Spring相比,这种新的支持涉及更少的配置,这当然是Spring Boot的主要目标之一。

3.1.应用程序属性:默认属性文件

Spring Boot 将其典型的配置约定应用于属性文件。这意味着我们可以简单地将 一个 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

请注意,对于属性文件,三段表示法前面有一个注释字符 ( # )。

3.2. 特定于环境的属性文件

如果我们需要针对不同的环境,Spring Boot 中有一个内置机制。我们可以简单地在 src/main/resources 目录中定义一个 application-environment.properties 文件,然后设置一个具有相同环境名称的 Spring 配置文件。

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

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

3.3. 测试属性文件

在测试应用程序时,我们可能还需要使用不同的属性值。Spring Boot 通过在测试运行期间查看我们的 src/test/resources 目录来为我们处理这个问题。同样,默认属性仍将正常注入,但如果发生冲突,则这些属性将被这些属性覆盖。

3.4.@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 注释 的属性参数来实现 类似的效果:

@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");
    }
}

3.5. 层次属性

如果我们有组合在一起的属性,我们可以利用 @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 再次应用其配置方法的约定,自动在属性名称及其相应字段之间映射。我们需要提供的只是属性前缀。

3.6. 替代方案:YAML 文件

Spring 还支持 YAML 文件。所有相同的命名规则都适用于特定于测试、特定于环境和默认属性文件。唯一的区别是文件扩展名和对类路径上的 SnakeYAML 库的依赖。YAML 特别适用于分层属性存储。

properties文件:

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

此外,在此版本中,无法再从特定于配置文件的文档激活配置文件,从而使结果更清晰、更可预测。

3.7. 导入其他配置文件

在 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/

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

3.8. 命令行参数的属性

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

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

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

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

3.9. 来自环境变量的属性

Spring 引导还将检测环境变量,将它们视为属性:

export name=value
java -jar app.jar

3.10. 属性值的随机化

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

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

4. 使用Raw Beans配置 —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;
}

5. 父子上下文中的属性

这个问题一次又一次地出现:当我们的 Web 应用程序具有父上下文和子上下文时会发生什么?父上下文可能具有一些共同的核心功能和 bean,然后是一个(或多个)子上下文,可能包含特定于 servlet 的 bean。在这种情况下,定义属性文件并将其包含在这些上下文中的最佳方法是什么?以及如何最好地从 Spring 中检索这些属性?

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

  • @Value 在子上下文中有效:是
  • @Value 在父上下文中工作:是
  • 环境.get属性 在子上下文中有效:是
  • 环境.get 属性在 上下文中有效:是

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

  • @Value 在子上下文中有效:是
  • @Value 在父上下文中有效:否
  • 环境.get属性 在子上下文中有效:是
  • 环境.get属性 在父上下文中有效:否