问题重现

某不知名springboot小项目,application.properties文件:

custom.param=中文属性值

java代码:

@SpringBootApplication
public class Application {@Value("${custom.param}")private String param;public static void main(String[] args) throws Exception {SpringApplication.run(Application.class, args);}@PostConstructpublic void printText() throws UnsupportedEncodingException {System.out.println(param);System.out.println(new String(param.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));}
}

控制台输出:

中文属性值
中文属性值

结论

先写结论:用@Value注解读取application.properties文件时,编码默认是ISO-8859-1,所以直接配置中文一定会乱码。注意,配置文件是springboot默认的配置文件application.properties或application-{active}.properties。其他配置文件会在原因分析中进行详解,原因分析涉及大量源码解读,如果不想烧脑深入分析的话可以直接跳到解决方案一节。

写在前面

写本文时,我查了网上几乎所有关于@Value读取.properties中文乱码的文章。一种思路是修改编码格式;另外一种是利用插件/IDE将中文预先编码,在注入到变量后直接转码为所需要的中文。遇到中文乱码修改编码方式是常规思路,所以第一种思路看似没问题,但是把springboot所有关于encoding的配置参数修改为UTF-8后,中文乱码的问题依然没有解决。

原因分析

Spring Boot版本:2.1.1.RELEASE

  • application.properties采用ISO-8859-1加载
  • 自定义test.properties可以设置编码格式
  • .yml/.yaml默认采用UTF-8加载

application.properties文件加载

正如前文所述读取配置文件时,编码出现了问题。追踪一下spring boot是加载默认配置文件的过程,会发现org.springframework.boot.contex.config.ConfigFileApplicationListener类的loadDocuments()方法,源码如下:

private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource) throws IOException {DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);List<Document> documents = this.loadDocumentsCache.get(cacheKey);if (documents == null) {List<PropertySource<?>> loaded = loader.load(name, resource);documents = asDocuments(loaded);this.loadDocumentsCache.put(cacheKey, documents);}return documents;
}

入参loader的类型是PropertySourceLoader,PropertySourceLoader是加载属性文件的接口,其实现有两个类:PropertiesPropertySourceLoader和YamlPropertySourceLoader。loader根据传入参数的实例调用load()方法,此处我们讨论.properties文件,接口声明和properties加载实现如下:

/*********属性文件加载接口**********/
public interface PropertySourceLoader {String[] getFileExtensions();List<PropertySource<?>> load(String name, Resource resource) throws IOException;
}
/*********properties文件加载实现**********/
public class PropertiesPropertySourceLoader implements PropertySourceLoader {private static final String XML_FILE_EXTENSION = ".xml";@Overridepublic String[] getFileExtensions() {return new String[] { "properties", "xml" };}@Overridepublic List<PropertySource<?>> load(String name, Resource resource) throws IOException {Map<String, ?> properties = loadProperties(resource);if (properties.isEmpty()) {return Collections.emptyList();}return Collections.singletonList(new OriginTrackedMapPropertySource(name, properties));}@SuppressWarnings({ "unchecked", "rawtypes" })private Map<String, ?> loadProperties(Resource resource) throws IOException {String filename = resource.getFilename();if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {return (Map) PropertiesLoaderUtils.loadProperties(resource);}return new OriginTrackedPropertiesLoader(resource).load();}}

通过源码分析PropertiesPropertySourceLoader并不单纯的加载.properties文件,还包含.xml文件(似乎有违单一功能原则,不知道当初这样设计的初衷是啥)。顺着load()方法向下找->loadProperties(Resource)->OriginTrackedPropertiesLoader.load()->OriginTrackedPropertiesLoader.load(boolean)->OriginTrackedPropertiesLoader$CharacterReader(Resource)。
CharacterReader是OriginTrackedPropertiesLoader的内部静态类,而且只有一个构造函数,看看器构造参数就不难发现为啥application.properties是以ISO-8859-1编码加载的了:

private static class CharacterReader implements Closeable {// 其他代码省略CharacterReader(Resource resource) throws IOException {this.reader = new LineNumberReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.ISO_8859_1));}// 其他代码省略
}

也就是说不论application.properties文件被设置为哪种编码格式,最终还是以ISO-8859-1的编码格式进行加载。

yml/yaml默认以UTF-8加载

让我们再看看yml/yaml格式的文件,其加载由PropertySourceLoader接口的另外一个实例YamlPropertySourceLoader实现,即接口方法load():

List<PropertySource<?>> load(String name, Resource resource) throws IOException;

追一下load()的底层实现,采用org.yaml.snakeyaml.reader.UnicodeReader的实例对yml/ymal文件进行加载,而UnicodeReader实例对文件的初始化方法init()实现如下:

protected void init() throws IOException {if (internalIn2 != null)return;Charset encoding;byte bom[] = new byte[BOM_SIZE];int n, unread;n = internalIn.read(bom, 0, bom.length);if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {encoding = UTF8;unread = n - 3;} else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {encoding = UTF16BE;unread = n - 2;} else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {encoding = UTF16LE;unread = n - 2;} else {// Unicode BOM mark not found, unread all bytesencoding = UTF8;unread = n;}if (unread > 0)internalIn.unread(bom, (n - unread), unread);// Use given encodingCharsetDecoder decoder = encoding.newDecoder().onUnmappableCharacter(CodingErrorAction.REPORT);internalIn2 = new InputStreamReader(internalIn, decoder);
}

每次调用read()读文件时都会调用init()方法进行初始化,也就是这个时候确定文件的编码格式。首先读取BOM(Byte Order Mark)文件头信息,如果头信息中有UTF8/UTF16BE/UTF16LE就采用对应的编码,没有或者不是则采用UTF8编码。

自定义test.properties文件编码

采用@PropertySource(value=“classpath:test.properties”, encoding=“UTF-8”)方式读取配置文件可按照UTF-8的方式读取编码,而不是ISO-8859-1。@PropertySource配置的加载文件由ConfigurationClassParser.processPropertySource()进行解析,EncodedResource类决定最后由哪种编码格式加载文件,其方法如下:

public Reader getReader() throws IOException {if (this.charset != null) {return new InputStreamReader(this.resource.getInputStream(), this.charset);}else if (this.encoding != null) {return new InputStreamReader(this.resource.getInputStream(), this.encoding);}else {return new InputStreamReader(this.resource.getInputStream());}}

所以,虽然都是.properties文件,但是编码格式却是不一样的。

解决方案

  • 自定义配置文件
  • 使用yml/yaml配置文件
  • IDE/插件预编码

自定义配置文件

通过@PropertySource(value=“classpath:my.properties”, encoding=“UTF-8”)注解配置自定义文件,注意文件名不能是springboot默认的application.properties文件名称。

使用yml/yaml配置文件

将yml/yaml文件设置为UTF-8的编码格式,springboot读该文件即采用UTF-8编码。

IDE/插件预编码

采用编译器或者插件将配置文件预编码。这种方法我没试过,但是想想也知道这是很反人类的。如果有人感兴趣的话,可以参考一下[这篇博客最后一部分IDEA/eclipse的修改操作]1

总结

在配置application.properties时,都是开发比较重要的参数,尽量使用英文,业务相关的中文配置还是不要放到这里。


  1. https://blog.csdn.net/m0_37995707/article/details/77506184 ‘Spring Boot自定义属性以及乱码问题’ ↩︎

SpringBoot使用@Value读取.properties中文乱码及解决方法相关推荐

  1. PHP读取TXT中文乱码的解决方法

    //$fname文件名称 if ($fname = $_FILES['nickname']['tmp_name']) { //file_get_contents() 函数把整个文件读入一个字符串中. ...

  2. python读取中文文件乱码-详解Python的json文件读取及中文乱码显示问题解决方法...

    Python的json文件读取及解决中文乱码显示问题 本文实例讲述了Python实现的json文件读取及中文乱码显示问题解决方法.分享给大家供大家参考,具体如下: city.json文件的内容如下: ...

  3. python json.loads()中文问题-Python实现的json文件读取及中文乱码显示问题解决方法...

    本文实例讲述了Python实现的json文件读取及中文乱码显示问题解决方法.分享给大家供大家参考,具体如下: city.json文件的内容如下: { "cities": [ { & ...

  4. java json utf-8_java读取json数据发生中文乱码的解决方法

    java读取json数据发生中文乱码的解决方法 发布时间:2020-06-21 15:58:48 来源:亿速云 阅读:268 作者:鸽子 java读取json数据出现乱码的代码://从json文件中读 ...

  5. ios html中文显示乱码,iOS读取txt文件出现中文乱码的解决方法

    一.情景描述: 后台给一个txt文件,编码是utf-8,在mac电脑xcode开发环境下读取txt文件内容,汉字会出现乱码,英文没有乱码这种情况. 二.尝试解决方法: 修改编码格式,尝试了nsutf1 ...

  6. idea控制台中文乱码的解决方法(最后一种亲测有效)

    idea控制台中文乱码的解决方法(三种,亲测有效) 问题情况: IntelliJ IDEA 控制台输出中文乱码部分如图所示: 解决方法: 方法一: 1.打开tomcat配置页面,Edit Config ...

  7. ASP+Access中文乱码的解决方法,ASP中文乱码,asp乱码问题

    确保编码声明正确(例:时尚淘女之家http://www.tao36524.com) 如果您使用的是国外空间,默认是西欧而不是GB2312,你可以通过强制为GD2312的方式! 在数据提取页面的第一行代 ...

  8. PHP输出中文乱码的解决方法

    PHP输出中文乱码的解决方法 参考文章: (1)PHP输出中文乱码的解决方法 (2)https://www.cnblogs.com/cyun/p/4146131.html 备忘一下.

  9. 织梦站内选择和文件管理器中文乱码的解决方法(utf8编码程序包才会)

    织梦站内选择和文件管理器中文乱码的解决方法(utf8编码程序包才会) 参考文章: (1)织梦站内选择和文件管理器中文乱码的解决方法(utf8编码程序包才会) (2)https://www.cnblog ...

最新文章

  1. java-在应用中获取spring定义的bean
  2. 手画:mvc三层框架生图
  3. 关于MySql链接url参数的设置 专题
  4. 【设计模式】中介者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
  5. 重磅更新!YoloV4最新论文与源码!权重!结构!翻译!
  6. Spring boot默认日志配置
  7. vue自定义组件并使用
  8. 引用js或css后加?v= 版本号的用法
  9. php下划虚线,什么是下划线符号
  10. Hbase写数据,存数据,读数据的详细过程
  11. python爬取酷狗音乐top500_Python爬取酷狗Top500的歌曲!够你吹个小牛皮了吧!
  12. asp.net截取字符串方法
  13. PhotoShop CS6破解方法及文件下载
  14. 详解DataStore,SharedPreferences终结者
  15. linux服务器6t硬盘分区,CentOS分区大于2TB的磁盘以及格式化大于16TB分区的解决方案...
  16. KMplayer保存上次播放位置
  17. 【ZOJ2750】Idiomatic Phrases Game(最短路)
  18. (纪中)2417. Loan Repayment【数学】
  19. macOS 系统打开软件时,会出现提示‘ “***.app”已损坏,无法打开。您应该推出磁盘映像。-解决方法
  20. matlab 电力电子元件对应名称,电力电子与MATLAB应用技术

热门文章

  1. Linux MISC 驱动实验-基于正点原子IMX6ULL开发板
  2. JavaScript 的入门学习案例,保证学会!
  3. 求职:你总得有什么可以“拿得出手”
  4. jQuery移动端日历考勤记录
  5. python3爬虫实战(三):mitmproxy对接python下载抖音小视频
  6. java 编译运行原理_Java编译和解释执行对比及原理解析
  7. Python 递归算 阶乘 法斐波那契数 不死兔问题
  8. 没有计算机学科评估的双非大学,软件专硕报录比超过8!天津师范大学计算机考研...
  9. python如何获取网络上的图片并将其保存在本地
  10. 二叉树系列二:二叉树的层序遍历(BFS)