Spring 是否遵循应用程序配置的命名约定?

Pan*_*kaj 9 java spring spring-boot

问题: application.properties 中定义的配置未被环境变量覆盖

我面临着 spring 配置的奇怪问题,因为application.properties当配置以特定方式命名时,中定义的配置不会被环境变量覆盖。正如外部化配置中提到的,操作系统环境变量优先application.properties,但当配置定义为时,这种情况不会发生,myExternal_url但当配置定义为时,它会起作用my_external_url(在下面的示例代码中,我们需要将配置更改为my_external_urlinApplicationProperties.javaapplication.properties

示例代码 -

@SpringBootApplication
public class ConfigApplication implements ApplicationRunner {

  @Autowired private ApplicationProperties applicationProperties;

  public static void main(String[] args) {
    SpringApplication.run(ConfigApplication.class, args);
  }

  @Override
  public void run(ApplicationArguments arg0) {
    System.out.println("External URL = " + applicationProperties.getMyExternalUrl());
  }
}
Run Code Online (Sandbox Code Playgroud)

应用程序 Bean 配置 -

@Configuration
public class AppConfig {

  @Bean
  @ConfigurationProperties(prefix = "")
  public ApplicationProperties applicationProperties() {
    return new ApplicationProperties();
  }
}
Run Code Online (Sandbox Code Playgroud)

ApplicationProperties班级 -

public class ApplicationProperties {

  @Value("${myExternal_url}")
  private String myExternalUrl;

  public String getMyExternalUrl() {
    return this.myExternalUrl;
  }

  public void setMyExternalUrl(String myExternalUrl) {
    this.myExternalUrl = myExternalUrl;
  }
}
Run Code Online (Sandbox Code Playgroud)

application.properties:

myExternal_url=external_url_env_application_properties
Run Code Online (Sandbox Code Playgroud)

这可能是什么原因?

编辑 - 添加 gradle Gradle 配置

plugins {
    id 'org.springframework.boot' version '2.4.0-M1'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
    maven { url 'https://repo.spring.io/milestone' }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    compileOnly 'org.projectlombok:lombok:1.18.6'
    annotationProcessor 'org.projectlombok:lombok:1.18.6'
}

test {
    useJUnitPlatform()
}
Run Code Online (Sandbox Code Playgroud)

编辑2

跟踪日志显示 myExternal_url 已从环境变量中正确解析。然后 Spring 尝试applicationProperties通过调用来解析自动装配的依赖项“ ” AutowiredAnnotationBeanPostProcessor,然后值被 application.properties 值覆盖(屏幕截图)。

o.s.c.e.PropertySourcesPropertyResolver  : Found key 'myExternal_url' in PropertySource 'systemEnvironment' with value of type String
o.s.c.e.PropertySourcesPropertyResolver  : Found key 'myExternal_url' in PropertySource 'environmentProperties' with value of type String
Run Code Online (Sandbox Code Playgroud)

调试应用程序属性

Pan*_*kaj 6

Spring 确实以宽松绑定的形式支持某种命名约定(如 @Kavithakaran Kanapathippillai 所指出的)。对于@ConfigurationProperties和 @Value 注释,Spring 尝试使用宽松绑定按照外部化配置中定义的顺序解析配置源中的变量

\n

正如回答 @\xe5\xa4\xa2\xe3\x81\xae\xe3\x81\xae\xe5\xa4\xa2 - spring 正确匹配使用@Value("${myExternal_url}")环境变量的属性,但后来被@ConfigurationProperties(prefix = "").

\n

由于 beanApplicationProperties被定义为@ConfigurationProperties(prefix = ""),Spring 尝试将变量与配置源进行匹配(使用宽松绑定),并在 application.propertied 变量中找到匹配项myExternalUrl,并覆盖使用 解析的属性@Value("${myExternal_url}")。问题在于同时使用@Value@ConfigurationProperties(prefix = "")

\n

旁注 -@Value支持有限的宽松绑定Spring 建议使用 kebab-case 定义 @Value 属性名称

\n

来自文档 -

\n
\n

如果您确实想使用@Value,我们建议您使用规范形式引用属性名称(仅使用小写字母)。这将允许 Spring Boot 使用与宽松绑定 @ConfigurationProperties 时相同的逻辑。例如,\n@Value("{demo.item-price}") 将从 application.properties 文件中获取 demo.item-price 和\ndemo.itemPrice 表单,以及从系统环境中获取\nDEMO_ITEMPRICE。如果您改用\n@Value("{demo.itemPrice}"),则不会考虑 demo.item-price 和 DEMO_ITEMPRICE\n。

\n
\n


夢のの*_*のの夢 4

TL;DR从注入的系统变量中@Value得到了正确的结果myExternal_Url,但其值后来由@ConfigurationProperties.

跟踪日志是正确的,因为 Spring 的排序将放在propertySource 列表systemEnvironment之前。classpath:/application.properties

您遇到的问题是由于同时使用@Value@ConfigurationProperties来注入/绑定您的属性。假设这些是您提供的值:

system:
  myExternal_Url: foo
  my_external_url: bar
applications.properties:
  myExternal_url: aaa
Run Code Online (Sandbox Code Playgroud)

在您的应用程序属性中:

  @Value("${myExternal_url}")
  private String myExternalUrl; // injected with foo
Run Code Online (Sandbox Code Playgroud)

myExternalUrl正确注入了foo您在环境变量中定义的值 ( )。但是,使用 setter 方法绑定之后@ConfigurationProperties的值。由于它使用宽松绑定,它会检查 的不同变体,它首先查找系统变量中的内容,并发现(驼峰和下划线)不属于宽松绑定形式之一,但随后(仅下划线)是。So的值提供给 setter:myExternalUrlmyExternal_urlmy_external_urlmy_external_url

public void setMyExternalUrl(String myExternalUrl) { // bar
  this.myExternalUrl = myExternalUrl; // myExternalUrl is reassigned from foo to bar
}
Run Code Online (Sandbox Code Playgroud)

因此应该清楚的是,您@Value总是会被覆盖,因为@ConfigurationProperties之后绑定了值。只需有:

public class ApplicationProperties {
    private String myExternalUrl;
    ...

Run Code Online (Sandbox Code Playgroud)

然后在您的系统中定义一种绑定形式 - MY_EXTERNAL_URLmy-external-urlmy_external_url(也许还有更多)。然后在 application.yml 中保持一致的大小写,以防您不需要系统变量。

my-external-url=aaa
Run Code Online (Sandbox Code Playgroud)

边注。建议您使用该表单MY_EXTERNAL_URL作为系统环境变量。