升级到SpringBoot2.0后 properties文件 中文乱码问题

升级到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

这就找出中文乱码的原因了。
问题是找到了那怎么解决呢??

  1. 方法一
    将properties中凡是是Unicode码全部改写为小写。啥,这么多文件要我一个一个改
  2. 方法二
    把springboot这部分的源码修改后重新打包。虽然可以办到,但还有更简单的么?
  3. 方法三
    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上已修复

猜你喜欢

转载自blog.csdn.net/u011928958/article/details/79697304