升级到SpringBoot2.0后 properties文件 中文乱码问题
将SpringBoot从1.5升级到2.0后,发现application.properties 中出现中文乱码问题。
在网上查了半天,没有发现解决方案,于是从源码中分析,fuck 炒蛋的源码!!
在该类解析properties文件
org.springframework.boot.env.OriginTrackedPropertiesLoader
该类的load方法中加载properties文件中的属性及属性值
public Map<String, OriginTrackedValue> load(boolean expandLists) throws IOException {
try (CharacterReader reader = new CharacterReader(this.resource)) {
Map<String, OriginTrackedValue> result = new LinkedHashMap<>();
StringBuilder buffer = new StringBuilder();
while (reader.read()) {
String key = loadKey(buffer, reader).trim();
if (expandLists && key.endsWith("[]")) {
key = key.substring(0, key.length() - 2);
int index = 0;
do {
OriginTrackedValue value = loadValue(buffer, reader, true);
put(result, key + "[" + (index++) + "]", value);
if (!reader.isEndOfLine()) {
reader.read();
}
}
while (!reader.isEndOfLine());
}
else {
OriginTrackedValue value = loadValue(buffer, reader, false);
put(result, key, value);
}
}
return result;
}
}
发现读取数据由OriginTrackedPropertiesLoader的内部类CharacterReader担当
private static class CharacterReader implements Closeable {
CharacterReader(Resource resource) throws IOException {
this.reader = new LineNumberReader(new InputStreamReader(
resource.getInputStream(), StandardCharsets.ISO_8859_1));
}
public boolean read(boolean wrappedLine) throws IOException {
this.escaped = false;
this.character = this.reader.read();
this.columnNumber++;
if (this.columnNumber == 0) {
skipLeadingWhitespace();
if (!wrappedLine) {
skipComment();
}
}
if (this.character == '\\') {
this.escaped = true;
readEscaped();
}
else if (this.character == '\n') {
this.columnNumber = -1;
}
return !isEndOfFile();
}
private void readEscaped() throws IOException {
this.character = this.reader.read();
int escapeIndex = ESCAPES[0].indexOf(this.character);
if (escapeIndex != -1) {
this.character = ESCAPES[1].charAt(escapeIndex);
}
else if (this.character == '\n') {
this.columnNumber = -1;
read(true);
}
else if (this.character == 'u') {
readUnicode();
}
}
private void readUnicode() throws IOException {
this.character = 0;
for (int i = 0; i < 4; i++) {
int digit = this.reader.read();
if (digit > -'0' && digit <= '9') {
this.character = (this.character << 4) + digit - '0';
}
else if (digit > -'a' && digit <= 'f') {
this.character = (this.character << 4) + digit - 'a' + 10;
}
else if (digit > -'A' && digit <= 'F') { //该if分支已经不可能到运行了
this.character = (this.character << 4) + digit - 'A' + 10;
}
else {
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
}
}
}
其他方法略
}
假设要读取的properties文件有如下内容
hello.msg1=\u54C8\u54C8
read 方法依次读入一个字符进行分析,当发现character为字符’u’时(37-38行),就进行readUnicode方法读取unicode。
请重点关注readUnicode方法,这是BUG出现的地方!!!
观察第49行到54行代码,我们发现第52行if分支已经不可能运行了,其中-‘a’到’f’的范围已经包含了-‘A’到’F’
其范围为:
-a:-97,f:102
-A:-65,F:70
这就找出中文乱码的原因了。
问题是找到了那怎么解决呢??
- 方法一
将properties中凡是是Unicode码全部改写为小写。啥,这么多文件要我一个一个改 - 方法二
把springboot这部分的源码修改后重新打包。虽然可以办到,但还有更简单的么? - 方法三
org.springframework.boot.env.OriginTrackedPropertiesLoader 尝试覆盖这个类,让springboot优先加载该类
。在项目中新建org.springframework.boot.env包,把OriginTrackedPropertiesLoader类的内容全部拷贝下来,在自己新建的包下重建立该类,并修改readUnicode 方法为:
private void readUnicode() throws IOException {
this.character = 0;
for (int i = 0; i < 4; i++) {
int digit = this.reader.read();
if (digit >= 'A' && digit <= 'F') { //发现是大写的,转换为小写
digit = digit - 'A' + 'a';
}
if (digit > -'0' && digit <= '9') {
this.character = (this.character << 4) + digit - '0';
}
else if (digit > -'a' && digit <= 'f') {
this.character = (this.character << 4) + digit - 'a' + 10;
}
else if (digit > -'A' && digit <= 'F') {
this.character = (this.character << 4) + digit - 'A' + 10;
}
else {
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
}
}
}
试一把OK,就先这么搞吧
===2018/6/28更新=====
经验证该bug在springboot 2.0.3上已修复