繁体   English   中英

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

[英]Does Spring follow any naming convention for application configuration?

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

我遇到了 spring 配置的奇怪问题,因为application.properties中定义的配置在以特定方式命名配置时不会被环境变量覆盖。 正如在外部化配置中提到的那样,操作系统环境变量优先于application.properties ,但是当配置定义为myExternal_url时不会发生这种情况,但当配置定义为my_external_url时它会起作用(在下面的示例代码中,我们需要在ApplicationProperties.java中将配置更改为my_external_url ApplicationProperties.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());
  }
}

应用程序 Bean 配置 -

@Configuration
public class AppConfig {

  @Bean
  @ConfigurationProperties(prefix = "")
  public ApplicationProperties applicationProperties() {
    return new ApplicationProperties();
  }
}

ApplicationProperties属性 class -

public class ApplicationProperties {

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

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

  public void setMyExternalUrl(String myExternalUrl) {
    this.myExternalUrl = myExternalUrl;
  }
}

application.properties

myExternal_url=external_url_env_application_properties

这可能是什么原因?

编辑 - 添加 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()
}

编辑 2

跟踪日志显示 myExternal_url 已从环境变量中正确解析。 然后 Spring 尝试通过调用AutowiredAnnotationBeanPostProcessor来解决自动装配的依赖项“ applicationProperties ”,然后值被 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

调试应用程序属性

    public class ApplicationProperties {

      private String myExternalUrl;

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

      public void setMyExternalUrl(String myExternalUrl) {
        this.myExternalUrl = myExternalUrl;
      }
    }  
  • 请注意,这两个属性仍然存在于Environment中,但是当它被绑定时,它似乎决定了一个是否覆盖另一个。 您可以通过打印看到这一点。 在这里,我从applicationContext获取environment ,但您可以autowire Environment并测试它
        System.out.println(context.getEnvironment()
                             .getProperty("myExternal_url"));
        System.out.println(context.getEnvironment()
                             .getProperty("my_external_url"));
  • 为了确认上述观点,只有@ConfigurationProperties支持宽松绑定,但不支持@Value 我创建了以下 class
   @Component
   public class ValueInjection {
    // This prints application properties.
    @Value("${myExternal_url}")
    private String myExternal_url;

    // This prints the environment variable
    @Value("${my_external_url}")
    private String my_external_url;

// If you uncomment this the application will not start saying 
// there is no such property.
// 
//    @Value("${myExternalUrl}")
//    private String myExternalUrl;

 //   ... getters and setters
    }

Spring 确实以轻松绑定的形式支持某种命名约定(正如@Kavithakaran Kanapathippillai 所指出的那样)。 @ConfigurationProperties和@Value 注释的情况下,Spring 尝试按照Externalized Configuration using Relax binding中定义的顺序从配置源解析变量。

正如@梦のの梦的回答 - spring 使用带有环境变量的@Value("${myExternal_url}")正确匹配属性,但后来被@ConfigurationProperties(prefix = "")覆盖。

由于 bean ApplicationProperties被定义为@ConfigurationProperties(prefix = "") ,Spring 尝试将变量与配置源匹配(使用宽松绑定)并在 application.properied 变量myExternalUrl中找到匹配项,并覆盖使用@Value("${myExternal_url}") 问题在于同时使用@Value@ConfigurationProperties(prefix = "")

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

从文档 -

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

TL;DR @Value从注入的系统变量中具有正确的myExternal_Url ,但其值稍后由@ConfigurationProperties设置。

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

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

system:
  myExternal_Url: foo
  my_external_url: bar
applications.properties:
  myExternal_url: aaa

在您的 ApplicationProperties 中:

  @Value("${myExternal_url}")
  private String myExternalUrl; // injected with foo

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

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

所以应该清楚你的@Value总是会被覆盖,因为@ConfigurationProperties会在之后绑定值。 只需拥有:

public class ApplicationProperties {
    private String myExternalUrl;
    ...

然后在您的系统中定义绑定 forms - MY_EXTERNAL_URLmy-external-urlmy_external_url (也许还有更多)之一。 然后在您的 application.yml 中使用一致的大小写,以防您不想要系统变量。

my-external-url=aaa

边注。 建议您使用MY_EXTERNAL_URL形式作为系统环境变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM