问题
application.yml
test:
password1: 030030
password2: '030030'
使用此配置的bean
@Component
public class Test {
@Value("${test.password1}")
private String password1;
@Value("${test.password2}")
private String password2;
@PostConstruct
public void postConstruct() {
System.out.println("password1 : " + password1);
System.out.println("password2 : " + password2);
}
}
结果
password1 : 12312
password2 : 030030
why?,为啥030030的int类型会被读取成12312 ??,而字符类型就正常呢?
思路
关键在org.yaml.snakeyaml.constructor.SafeConstructor.ConstructYamlInt
类中,此类是springboot读取Int类型配置的处理类。当然还有其他类型
从结构和类的命令中可以看到是在读取yml文件中对各种数据类型都有相应的处理类。下面我们来直接分析Int类型的处理类ConstructYamlInt
ConstructYamlInt
public class ConstructYamlInt extends AbstractConstruct {
@Override
public Object construct(Node node) {
//获取到配置的值030030
String value = constructScalar((ScalarNode) node).toString().replaceAll("_", "");
int sign = +1;
//firtst为'0'
char first = value.charAt(0);
if (first == '-') {
sign = -1;
value = value.substring(1);
} else if (first == '+') {
value = value.substring(1);
}
int base = 10;
if ("0".equals(value)) {
return Integer.valueOf(0);
//如果value以0b开头,则认为value是2进制
} else if (value.startsWith("0b")) {
value = value.substring(2);
base = 2;
//如果value以0x开头,则认为value是16进制
} else if (value.startsWith("0x")) {
value = value.substring(2);
base = 16;
//如果value以0开头,则认为value是8进制
//030030就会进入此if条件
} else if (value.startsWith("0")) {
//最前面的0被截取掉变成了30030
value = value.substring(1);
base = 8;
} else if (value.indexOf(':') != -1) {
String[] digits = value.split(":");
int bes = 1;
int val = 0;
for (int i = 0, j = digits.length; i < j; i++) {
val += Long.parseLong(digits[j - i - 1]) * bes;
bes *= 60;
}
return createNumber(sign, String.valueOf(val), 10);
} else {
return createNumber(sign, value, 10);
}
//最后会执行到这里
//sign = 1
//value = 30030
//base = 8
return createNumber(sign, value, base);
}
}
createNumber(sign, value, base)
private Number createNumber(int sign, String number, int radix) {
//得到number的长度,30030也就是长度为5
final int len = number != null ? number.length() : 0;
if (sign < 0) {
number = "-" + number;
}
//这里得到的maxArr = [11,21]
final int[] maxArr = radix < RADIX_MAX.length ? RADIX_MAX[radix] : null;
if (maxArr != null) {
final boolean gtInt = len >maxArr[0];
if (gtInt) {
if(len > maxArr[1]) {
return new BigInteger(number, radix);
}
return createLongOrBigInteger(number, radix);
}
}
Number result;
try {
//这里将30030当做八进制来转化为十进制后返回也就是12312
result = Integer.valueOf(number, radix);
} catch (NumberFormatException e) {
result = createLongOrBigInteger(number, radix);
}
return result;
}
所以就解释了为啥030030在被读取后会变成12312
总结
本质原因就是配置值以0开头的,所以被yml解析时认为value是8进制的,帮我们转换成了10进制,所以导致读取后的值和开始配置的值对不上。
解决起来很简单,就是在yml中配置数字时,避免0、0b、0x开头。如果无法避免,则使用字符类型,因为字符类型不会做处理。
ConstructYamlStr
public class ConstructYamlStr extends AbstractConstruct {
@Override
public Object construct(Node node) {
return constructScalar((ScalarNode) node);
}
}
详细的springboot配置解析,可参考此文章详解SpringBoot解析yml全流程