Java 在不同情况下使用枚举值的ConfigurationProperties
有一种行为我找不到相关文档。 让我们假设以下代码。它应该在控制台中显示使用foo.bar属性配置的内容:Java 在不同情况下使用枚举值的ConfigurationProperties,java,spring,spring-boot,Java,Spring,Spring Boot,有一种行为我找不到相关文档。 让我们假设以下代码。它应该在控制台中显示使用foo.bar属性配置的内容: @SpringBootApplication @Component public class Test { @Autowired TestConfig testConfig; public static void main(String[] args) throws Exception { ConfigurableApplicationContext
@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;
}
}
}
@springboot应用程序
@组成部分
公开课考试{
@自动连线
TestConfig TestConfig;
公共静态void main(字符串[]args)引发异常{
ConfigurableApplicationContext run=新的SpringApplication(Test.class).run(args);
Test=run.getBean(Test.class);
test.run();
}
public void run()引发异常{
testConfig.getBar().entrySet().forEach(e->{
System.out.println(e.getKey()+“”+e.getValue());
});
}
@配置
@ConfigurationProperties(ignoreUnknownFields=false,前缀=“foo”)
静态类TestConfig{
私有映射栏=新的HashMap();
公共地图getBar(){
返回杆;
}
公共空白立根(地图栏){
这个.bar=bar;
}
}
}
如果您在application.yml(foo.bar[A_值]:from application.yml
)中设置了以下属性,它将被正确拾取,并在控制台中显示“from application.yml”,而不显示任何内容
现在,如果您使用完全相同的代码,但这次您希望使用命令行参数覆盖application.yml中定义的属性,并将--foo.bar[aValue]=“from command line”
设置为命令行参数(请注意,这次我对枚举引用使用了camel大小写)。它仍然显示在控制台中的“from application.yml”,而不是覆盖的属性
如果我在命令行中选择大写枚举,在application.yml中选择驼峰大小写枚举,它仍然会向控制台显示相同的内容
这是预期的行为吗?
在这种情况下,规则是什么
根据我的测试,它与中描述的完全相反
我已经用spring boot 1.2.5.RELEASE和1.3.0.RELEASE进行了测试
感谢您抽出时间Spring使用
StringToEnum
将字符串值转换为enum。此类内部使用java.lang.Enum#valueOf
方法进行转换。Enum类创建一个映射,然后在此映射上执行查找。因此,键必须与查找成功的确切情况匹配
以下测试用例将验证:
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}
第一个和第四个场景-由于键名称完全相同,因此设置了第一个命令行属性。此属性被添加到已处理列表中,因此YML属性被忽略
第二和第三种方案-由于键名称不同,因此会处理命令行和YML属性。第二个正在处理的YML将覆盖从命令行设置的值。在配置属性中,最后一个将获胜:
- application.yaml(1)
- application.yaml(2)
- JVM otpions
-Dfoo.bar.B_VALUE=b11-Dfoo.bar.cvvalue=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
// 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
}]
}
恐怕这个答案不对。这两个属性的定义在Spring中都是正确的,分别是application.yml和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)
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
}]
}
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()