signed

QiShunwang

“诚信为本、客户至上”

成功解决微服务@Value获取配置文件乱码问题

2021/5/14 21:04:47   来源:

问题描述

微服务中获取properties时:

mystyle.station.content = 测试中文字符

相关代码:

@Slf4j
public class GetPropertiesValueDemo {

    @Value("${mystyle.station.content}")
    private String content;

    @PostConstruct
    public String getCreateContent(){
        try {
            log.info(this.getClass().getSimpleName() +"_getCreateContent starts,content:{}",content);
            String utf8Content = new String(content.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
            log.info(this.getClass().getSimpleName() +"_getCreateContent starts,utf8Content:{}",utf8Content);
        } catch (Exception e) {
            log.error("e:{}",e);
        }
        return "success";
    }
}

在用@Value获取值时出现乱码问题。

GetPropertiesValueDemo_getCreateContent starts,content:å°Šæ•¬çš„ç”¨æˆ·ï¼Œæ‚¨çš„æ‰‹æœºå·²æ¬ è´¹ï¼Œè¯·æ‚¨åŠæ—¶äº¤è´¹ã€‚è¯¦æƒ
请拨打<电话>。 

原因分析

结论

通过源码分析和相关文章可以知道,application.properties默认是ISO-8859-1格式来加载的。

查看源码的路径如下:底层源码jar包为:

  • org.springframework.boot:spring-boot:2.1.3.RELEASE

查看类的相关代码顺序:

ConfigFileApplicationListener loadDocuments -->PropertySourceLoader load --> PropertiesPropertySourceLoader loadProperties -->OriginTrackedPropertiesLoader load --> OriginTrackedPropertiesLoader CharacterReader 

具体源码分析如下:

ConfigFileApplicationListener类

类所在地址为:org.springframework.boot.context.config。

此类作用:加载配置文件的属性,可以加载”application.properties“和”application.yml“的文件属性,也可以加载获取活动配置的文件。

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;
		}

PropertiesPropertySourceLoader类

此类作用:将”.properties“文件加载到PropertySource中。

区分调用是.xml文件结尾还是其他文件类型,然后调用不同的方法。

public class PropertiesPropertySourceLoader implements PropertySourceLoader {

	private static final String XML_FILE_EXTENSION = ".xml";

	@Override
	public String[] getFileExtensions() {
		return new String[] { "properties", "xml" };
	}

	@Override
	public 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();
	}

}

OriginTrackedPropertiesLoader类

此类作用:将含”.properties“结尾的文件加载到map中,当然也支持list类型。

public Map<String, OriginTrackedValue> load(boolean expandLists) throws IOException {
		try (CharacterReader reader = new CharacterReader(this.resource)) {}
  	...
}

其中load()方法中CharacterReader的字符类型指定为ISO_8859_1格式。

CharacterReader(Resource resource) throws IOException {
			this.reader = new LineNumberReader(new InputStreamReader(
					resource.getInputStream(), StandardCharsets.ISO_8859_1));
		}

从这里就可以知道为何会出现乱码了。

解决办法

以下方法都可以解决乱码问题。

用apollo直接配置中文的内容

直接在apollo客户端配置中文字符,则可以解决。

这次我直接用的这个办法,方便快捷。

用new String()来转换为UTF-8

代码如下:

String utf8Content = new String(content.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8)

注意点

此处在配置时如果用到了apollo,后续变更变量内容,直接在apollo中修改,则在转换时因apollo已经转换为了utf-8,则会出现转换问题。

在启动类配置@PropertySource并指定配置文件和UTF-8格式

其实就是@PropertySource注解指定了配置文件和编码格式。

在启动类中加上以下配置:

@PropertySource(value = "classpath:test.properties", encoding="UTF-8")

然后新增一个test.properties配置文件,将此配置写到mystyle.station.content这个配置即可。

特别说明

新增的配置文件不能用application.properties,否则还是乱码。

.yml/.yaml默认采用UTF-8加载

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

查看源码可知,初始化时会确定文件的编码格式。首先读取BOM(Byte Order Mark)文件头信息,如果头信息中有UTF8/UTF16BE/UTF16LE就采用对应的编码,没有或者不是则采用UTF8编码。

完美解决!

参考和推荐文章

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