当前位置: 首页 > 知识库问答 >
问题:

配置属性与Enum值在不同的情况下

楮景明
2023-03-14

有一个行为我找不到相关的留档。让我们假设以下代码。它应该在控制台中显示使用foo.bar属性配置的内容:

@SpringBootApplication
@Component
public class Test {
    @Autowired
    TestConfig testConfig;

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext run = new SpringApplication(Test.class).run(args);
        Test test = run.getBean(Test.class);
        test.run();
    }

    public void run() throws Exception {
        testConfig.getBar().entrySet().forEach(e -> {
            System.out.println(e.getKey() + " " + e.getValue());
        });
    }

    @Configuration
    @ConfigurationProperties(ignoreUnknownFields = false, prefix = "foo")
    static class TestConfig {
        private Map<SomeEnum, String> bar = new HashMap<>();

        public Map<SomeEnum, String> getBar() {
            return bar;
        }

        public void setBar(Map<SomeEnum, String> bar) {
            this.bar = bar;
        }
    }
}

如果您在application.yml(foo.bar[A_VALUE]: fromapplication.yml)中设置了以下属性,它将被正确地拾取并在控制台中显示“fromapplication.yml”,没什么花哨的

现在,如果您使用完全相同的代码,但这次您想使用命令行参数覆盖application.yml中定义的属性,并将--foo.bar[aValue]="from命令行"设置为命令行arg(请注意,这次我使用骆驼大小写作为枚举引用)。它仍然在控制台中显示"fromapplication.yml",而不是覆盖的属性。

如果我在命令行中选择大写枚举,在application.yml中选择骆驼大写枚举,它仍然会向控制台显示相同的内容。

这是预期的行为吗?在这种情况下的规则是什么?

根据我的测试,它与https://docs . spring . io/spring-boot/docs/current/reference/html/boot-features-external-config . html # boot-features-external-config中描述的完全相反

我已经用Spring启动 1.2.5.发布和 1.3.0.发布进行了测试

谢谢你的时间

共有2个答案

呼延源
2023-03-14

在配置属性中,最后一个获胜:

>

  • 应用程序。yaml(1)

    foo.bar.A_VALUE : 111  
    foo.bar.aValue  : 222    # output '222', overrieded
    

    应用程序. yaml (2)

    foo.bar.aValue  : 222   
    foo.bar.A_VALUE : 111    # output '111', overrieded 
    

    因此,当在PropertiesConfigurationFactory#doBindProperties ToTarget()(我使用的是spring boot 1.5.2)进行调试时,使用:

    >

  • JVM otpions-Dfoo.bar.B_VALUE=b11-Dfoo.bar.cValue=c11
  • application.yml:

    foo.bar:
      A_VALUE: aaa
      B_VALUE: bbb
      C_VALUE: ccc
      D_VALUE: dddd
      dValue: ddd
    
    logging.level:
      org.springframework.boot.env: TRACE
      org.springframework.boot.context.config: DEBUG
    

    propertyValues#propertyValues 这是 LinkedHashMap,具有以下属性键顺序:

    // keys are unique, when same key, systemProperties take first.
    0. `foo.bar.B_VALUE`  from 'systemProperties'
    1. `foo.bar.cValue`   from 'systemProperties'
    2. `foo.bar.A_VALUE`  from 'applicationConfig: [classpath:/application.yml]'
    3. `foo.bar.C_VALUE`  from 'applicationConfig: [classpath:/application.yml]'
    4. `foo.bar.D_VALUE`  from 'applicationConfig: [classpath:/application.yml]'
    5. `foo.bar.dValue`   from 'applicationConfig: [classpath:/application.yml]'
    

    控制台输出为:

    B_VALUE b11  // systemProperties first 
    A_VALUE aaa  
    D_VALUE ddd  // the last one wins. (foo.bar.dValue) 
    C_VALUE ccc  // ths last one wins. (foo.bar.C_VALUE)
    

    在我的测试中,使用JSON符号:

    PropertiesConfigurationFactory#propertySources = {
        class : ConfigurationPropertiesBindingPostProcessor$FlatPropertySources
        propertySources : [ { 
            class : PropertySourcesPlaceholderConfigurer$1
            name  : 'environmentProperties',
            source: {
                class           : StandardServletEnvironment,
                propertySource  : {
                    class               : MutablePropertySources,
                    propertySourceList  : [{
                        class: PropertySource$StubPropertySource,
                        name : 'servletConfigInitParams'
                    }, {
                        class: MapPropertySource,
                        name : 'systemProperties'
                    }, {
                        class: SystemEnvironmentPropertySource,
                        name : 'systemEnvironment'
                    }, {
                        class: RandomValuePropertySource,
                        name : 'random'
                    }, {
                        class: MapPropertySource,
                        name : 'applicationConfig: [classpath:/application.yml]'
                    }, {
                        class: MapPropertySource,
                        name : 'refresh'
                    }]
                }
            }
        }, {
            class : PropertiesPropertySource,
            name  : 'localProperties',
            source: <Properties>  // empty in my test
        }]
    }
    

    注意:类<code>PropertiesConfigurationFactory</code>已在spring boot 2.x中删除。

    PS:当搜索这个问题时,我想弄清楚Enum值(如A_VALUE)可以写入配置属性中的概念。答案正如@Moit所说。

    RelaxedDataBinder#bind()
      RelaxedConversionService#convert()
        1. try DefaultConvertionService#convert()
    
           # only support `A_VALUE`
           StringToEnumConverterFactory#StringToEnum#convert()
    
        2. then GenericConversionService#convert()
    
           # the config key can be :
           # 0 = "a-value"
           # 1 = "a_value"
           # 2 = "aValue"
           # 3 = "avalue"
           # 4 = "A-VALUE"
           # 5 = "A_VALUE"
           # 6 = "AVALUE"
           RelaxedConversionService$StringToEnumIgnoringCaseConverterFactory$StringToEnum#convert() 
    

  • 壤驷子安
    2023-03-14

    Spring使用StringToEnum将字符串值转换为枚举。该类在内部使用java.lang.Enum#valueOf方法进行转换。枚举类创建一个映射,然后在此映射上执行查找。因此,密钥必须与查找成功的确切大小写匹配。

    以下测试用例将验证:

    enum SomeEnum{
        A, B
    }
    
    public class EnumTest {
    
        public static void main(String[] args) {
            SomeEnum e1 = Enum.valueOf(SomeEnum.class, "A");
            System.out.println(e1);
            SomeEnum e2 = Enum.valueOf(SomeEnum.class, "a"); //throws exception
        }
    }
    

    因此,当spring无法转换从命令行传递的值时,它会返回到application.yml中定义的值。

    编辑

    如果您尝试以下组合:

    foo.bar[A_VALUE]: from application.yml
    foo.bar[A_VALUE]: from command line
    {A_VALUE=from command line}
    
    foo.bar[A_VALUE]: from application.yml
    foo.bar[aValue]: from command line
    {A_VALUE=from application.yml}
    
    foo.bar[aValue]: from application.yml
    foo.bar[A_VALUE]: from command line
    {A_VALUE=from application.yml}
    
    foo.bar[aValue]: from application.yml
    foo.bar[aValue]: from command line
    {A_VALUE=from command line}
    

    第一

    第二

     类似资料:
    • 问题内容: 我的应用程序需要使用.properties文件进行配置。在属性文件中,允许用户指定路径。 问题 属性文件需要转义的值,例如 需要的 我需要某种方法来接受不转义值的属性文件,以便用户可以指定: 问题答案: 为什么不简单地扩展属性类以合并双正斜杠的剥离。这样做的一个好功能是,在程序的其余部分中,您仍然可以使用原始类。 使用新类很简单: 剥离代码也可以进行改进,但是总的原理就在那里。

    • 问题内容: 我定义了一个独立的枚举类型,如下所示: 现在,我想将该类型的值注入bean属性: …那行不通:( 我应该如何将枚举注入spring bean? 问题答案: 你是否尝试过“ TYPE1”?我想Spring还是要使用反射来确定“类型”的类型,因此完全限定的名称是多余的。Spring通常不接受冗余!

    • 问题内容: 我正在使用不同的Maven配置文件将我的应用程序部署到不同的环境。(使用weblogic-maven-plugin,但是我认为这并不重要) 在应用程序中,我使用Spring Web Services。现在,我想根据环境更改端点。(端点在Spring的applicationContext.xml中定义) 我的想法是从属性文件中读取值。该属性文件将在Mavens软件包阶段写入(或复制)。

    • 问题内容: 我有一个定义自己的枚举的类,如下所示: 如果指定MyEnum.E1,它可以正常工作,但我真的很想将其作为“ E1”。您知道如何实现此目的,还是必须在另一个文件中定义它才能起作用? 结论:我无法正确获取导入的语法。由于有几个答案表明这是可能的,因此我将选择一个为我提供所需语法的文件,并对其他文件进行投票。 顺便说一下,这是一个真正的STRANGE部分(在我执行静态导入之前),我编写的使用

    • 我使用下面的代码(来自这个答案)来配置要记录在WebClient请求上的头: 这很管用,但感觉有点奇怪。问题是:我是否需要像这样包含Jackson/objectMapper配置,或者是否有更简单的方法来避免Spring objectMapper配置被覆盖?

    • 我希望将concur严格用作配置源。我正在使用spring cloud Consor配置来获取配置。我正在使用git2consul将文件加载到Consor并读取它们。根据spring云文档,我在构建中添加了以下内容。格拉德尔 并在我的application.properties 我面临的问题是,预期的属性没有加载到ConfigurationProperties bean中。在ConsultProp