Springboot bootstrap.properties 配置文件中文乱码

19 篇文章 0 订阅
7 篇文章 0 订阅

Springboot 在默认配置文件 bootstrap.properties 中使用中文会导致乱码,无法识别,但是如果使用 unicode 编码方式进行书写,则可以正确识别。 如果将 .properties 换成 .yml 也可以识别中文,只有 .properties 无法正确识别。

这是因为 .properties 文件的默认读取配置是使用的 ISO_8859_1 编码,这个编码是不支持中文的。
如果不是默认配置文件,可以使用 @PropertySource 指定配置文件的编码方式,让他支持 UTF-8。
例如: @PropertySource(value = {"classpath:info.properties"}, encoding = "UTF-8")
但是默认的 bootstrap.properties 无法识别 UTF-8,查询了好多方式都无法起效。

1. Springboot 在默认配置文件加载

查询源码:
bootstrap.properties 是在 org.springframework.boot.context.config.ConfigFileApplicationListener 中被读取。
具体其实是走 postProcessEnvironmentaddPropertySources,然后通过 new Loader(environment, resourceLoader).load() 实现。
源码如下:

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		addPropertySources(environment, application.getResourceLoader());
	}

	private void onApplicationPreparedEvent(ApplicationEvent event) {
		this.logger.switchTo(ConfigFileApplicationListener.class);
		addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
	}

	/**
	 * Add config file property sources to the specified environment.
	 * @param environment the environment to add source to
	 * @param resourceLoader the resource loader
	 * @see #addPostProcessors(ConfigurableApplicationContext)
	 */
	protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
		RandomValuePropertySource.addToEnvironment(environment);
		new Loader(environment, resourceLoader).load();
	}

然后继续跟代码


		private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
				DocumentConsumer consumer) {
			try {
				Resource resource = this.resourceLoader.getResource(location);
				if (resource == null || !resource.exists()) {
					if (this.logger.isTraceEnabled()) {
						StringBuilder description = getDescription("Skipped missing config ", location, resource,
								profile);
						this.logger.trace(description);
					}
					return;
				}
				if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
					if (this.logger.isTraceEnabled()) {
						StringBuilder description = getDescription("Skipped empty config extension ", location,
								resource, profile);
						this.logger.trace(description);
					}
					return;
				}
				String name = "applicationConfig: [" + location + "]";
				List<Document> documents = loadDocuments(loader, name, resource);
				if (CollectionUtils.isEmpty(documents)) {
					if (this.logger.isTraceEnabled()) {
						StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
								profile);
						this.logger.trace(description);
					}
					return;
				}
				List<Document> loaded = new ArrayList<>();
				for (Document document : documents) {
					if (filter.match(document)) {
						addActiveProfiles(document.getActiveProfiles());
						addIncludedProfiles(document.getIncludeProfiles());
						loaded.add(document);
					}
				}
				Collections.reverse(loaded);
				if (!loaded.isEmpty()) {
					loaded.forEach((document) -> consumer.accept(profile, document));
					if (this.logger.isDebugEnabled()) {
						StringBuilder description = getDescription("Loaded config file ", location, resource, profile);
						this.logger.debug(description);
					}
				}
			}
			catch (Exception ex) {
				throw new IllegalStateException("Failed to load property " + "source from location '" + location + "'",
						ex);
			}
		}

进入到当前代码里面,发现实现是在 List<Document> documents = loadDocuments(loader, name, resource);
继续跟代码,发现 loaderthis.propertySourceLoaders 遍历得到的,最终发现是 this.propertySourceLoaders 里面有一个类 org.springframework.boot.env.PropertiesPropertySourceLoader 实现的 properties 加载,查看这个类


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

实现是 return new OriginTrackedPropertiesLoader(resource).load(); 进入这里面发现读取是通过 try (CharacterReader reader = new CharacterReader(this.resource)) {,查看 CharacterReader ,发现这里面编码是写死的。


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

因为这里面是写死的,所以想要通过配置的方法来实现默认配置文件能够读取中文是无法实现的,只能重写PropertiesPropertySourceLoader类。

2. 重写PropertiesPropertySourceLoader

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

/**
 * @author ******
 */
public class UnicodePropertySourceLoader implements PropertySourceLoader {
    Logger logger = LoggerFactory.getLogger(UnicodePropertySourceLoader.class);

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

    @Override
    public List<PropertySource<?>> load(String name, Resource resource) throws IOException {

        Properties properties = getProperties(resource);
        if (!properties.isEmpty()) {
            PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource(name, properties);
            return Collections.singletonList(propertiesPropertySource);
        }

        return Collections.emptyList();
    }

    private Properties getProperties(Resource resource) {
        Properties properties = new Properties();
        try (InputStream inputStream = resource.getInputStream()) {
            properties.load(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
        } catch (IOException e) {
            logger.error("Load resource inputStream failed.", e);
        }
        return properties;
    }
}

在 resources 下新建文件夹 META-INF,然后新建文件 spring.factories,
在里面将类加入进去。
org.springframework.boot.env.PropertySourceLoader=com.znv.peim.UnicodePropertySourceLoader
配置

3. 效果

项目结构
项目结构
乱码
乱码

成功
中文成功
说明:在 ApplicationRunner 下的 统一监控中心 是使用的默认配置,而这是一个神奇的中文则是 info.properties 内配置的,是否中文显示由 @PropertySource(value = {"classpath:info.properties"}, encoding = "UTF-8") 控制

最终结果

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值