有一种行为无法找到相关文档。 我们假设以下代码。它应该在控制台中显示已使用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]: from application.yml
)中设置了以下属性,它将被正确选中并在控制台中显示“from application.yml”,没什么特别的
现在,如果你使用完全相同的代码,但这次你要用命令行参数覆盖application.yml中定义的属性,并设置 - foo.bar[aValue]="from command line"
作为命令行arg(注意这次我使用骆驼案例作为枚举参考)。它仍然在控制台中显示“来自application.yml”而不是被覆盖的属性。
如果我在application.yml的命令行和camel case枚举中选择了大写枚举,它仍然会向控制台显示相同的内容。
这是预期的行为吗? 在这种情况下有什么规则?
中描述的完全相反我已经使用spring boot 1.2.5.RELEASE和1.3.0.RELEASE进行了测试
感谢您的时间
答案 0 :(得分:2)
在配置属性中,最后一个获胜:
application.yaml(1)
foo.bar.A_VALUE : 111
foo.bar.aValue : 222 # output '222', overrieded
application.yaml(2)
foo.bar.aValue : 222
foo.bar.A_VALUE : 111 # output '111', overrieded
因此,在PropertiesConfigurationFactory#doBindPropertiesToTarget()进行调试时(我使用spring-boot 1.5.2),使用:
-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
}]
}
注意:类PropertiesConfigurationFactory
已在spring boot 2.x中删除。
PS:在这个问题上搜索时,我想弄清楚可以在配置属性中写入哪些概念Enum值(例如A_VALUE)。答案正如@Mohit所说的那样。
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()
答案 1 :(得分:0)
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}
1st&amp;第4种情况 - 由于键名完全相同,因此设置了第一个命令行属性。此属性将添加到已处理的列表中,因此将忽略YML属性。
第二&amp;第三种情况 - 由于关键名称不同,命令行和处理YML属性。正在处理的YML将覆盖从命令行设置的值。