SpringBoot读取application.properties中文乱码的解决办法
1.问题描述由于ye
1.问题描述
由于业务需求需要在application.properties中配置一个带有中文字符串的参数,注入到业务类中,但是发现注入的中文是乱码的。大概情况如下所示:
@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class UnitTest {
@Value("${name}")
private String name;
@Test
public void test1() throws UnsupportedEncodingException {
System.out.println("中文内容:" + name);
}
}
打印输出结果:
2.问题解决
2.1网络上已有的解决办法
看到这个问题,第一时间去网上博客搜索。网络上的解决办法基本一致,概括为以下三种。
2.1.1修改IDEA编码
在IDEA中将所有的编码设置为UTF-8同时勾上一个叫Transparent native-to-ascii conversion的选项,然后重新创建application.properties的文件
运行结果:
但是这个配置文件几乎无法再使用记事本修改了。。。用记事本打开编辑时,发现内容被修改成了unicode编码,在线上部署时几乎是绝望的。。。所以此方式我不做推荐。
2.1.2将application.properties改为application.yml
就是将application.properties的文件修改为application.yml的结构,重启项目。
运行效果
证明是可行的。这种方式可以根据自己需求选择,但是当配置文件的内容层级较深时也不推荐,容易看错行配置。
2.1.3在读取properties文件的时候设置编码
@Configuration
@PropertySource(value = "classpath:application.properties", encoding = "utf-8")
public class MyConfig {
@Value("${name}")
private String name;
@PostConstruct
public void init() {
System.out.println("name is :" + name);
}
}
亲测发现这种方式针对application.properties是不行的。
但是针对其他名称的properties文件是可以的
@Configuration
@PropertySource(value = "classpath:test.properties", encoding = "utf-8")
public class MyConfig2 {
@Value("${name2}")
private String name;
@PostConstruct
public void init() {
System.out.println("name2 is :" + name);
}
}
运行结果
2.2编码层面解决办法(个人研究的)
2.2.1研究过程(不喜欢的直接看2.2.2解决办法)
以上网络上的解决办法,都不太适合个人的话,请留意这一种终极解决办法,本人研究得出的,如有问题望各位大佬评论指证。通过阅读源码发现在ConfigFileApplicationListener中有如下代码
private PropertySource<?> doLoadIntoGroup(String identifier, String location,
Profile profile) throws IOException {
Resource resource = this.resourceLoader.getResource(location);
PropertySource<?> propertySource = null;
StringBuilder msg = new StringBuilder();
if (resource != null && resource.exists()) {
String name = "applicationConfig: [" + location + "]";
String group = "applicationConfig: [" + identifier + "]";
//重点就在这里,可以在这里打个断点,发现在这里就已经把application.properties的内容读取到了
propertySource = this.propertiesLoader.load(resource, group, name,
(profile == null ? null : profile.getName()));
...
}
好了,那么进入这个方法内部看
进入PropertiesPropertySourceLoader查看load方法
public PropertySource<?> load(String name, Resource resource, String profile)
throws IOException {
if (profile == null) {
//好了 我们发现properties实在这里加载的
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
if (!properties.isEmpty()) {
return new PropertiesPropertySource(name, properties);
}
}
return null;
}
断点查看执行效果
那么可以发现问题就是出在这个PropertiesLoaderUtils.loadProperties(resource)内部了,进入查看内部调用过程是PropertiesLoaderUtils.loadProperties(resource)->fillProperties()
public static void fillProperties(Properties props, Resource resource) throws IOException {
InputStream is = resource.getInputStream();
try {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(".xml")) {
props.loadFromXML(is);
} else {
//问题就在这里,这里使用的是字节流,没有指定字符编码 那么读取时将会以ISO-8859-1来进行编码
props.load(is);
}
} finally {
is.close();
}
}
从流程可以看出 application是没有指定编码的,那么是不是可以尝试修改fillProperties的源码解决,但这样改变了源代码,还得重新打包。让我们回到PropertySourcesLoader这个类,刚才发现这个类加载了两个类来进行具体的配置文件解析,PropertiesPropertySourceLoader和YamlPropertySourceLoader那么能不能让这个类拿到我们自定义的Properties的解析文件的类呢?那么重点就在于PropertySourcesLoader.loaders的构造了。找遍整个类发现只有一个地方对该属性赋值了
public PropertySourcesLoader(MutablePropertySources propertySources) {
Assert.notNull(propertySources, "PropertySources must not be null");
this.propertySources = propertySources;
//唯一对loaders赋值的地方
this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
打个断点观察先
果然就是在这里注入了两类文件解析的配置,进入方法内部发现另一个关键点
继续进入loadFactoryNames方法内部查看
发现首先会去找classpath下面的META-INF/spring.factories中找org.springframework.boot.env.PropertySourceLoader的实现类,而且这个默认配置在spring-boot-version.jar中存在,那么把配置文件拿过来在本地的配置中重写一下是不是可以呢?
原本的配置文件
2.2.2解决办法
1.创建一个类继承PropertiesPropertySourceLoader
public class SelfPropertySourceLoader extends PropertiesPropertySourceLoader {
@Override
public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
if (profile == null) {
// Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Properties properties = loadUseUtf8(resource);
if (!properties.isEmpty()) {
return new PropertiesPropertySource(name, properties);
}
}
return null;
}
private Properties loadUseUtf8(Resource resource) throws IOException {
Properties props = new Properties();
InputStream is = resource.getInputStream();
try {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(".xml")) {
props.loadFromXML(is);
} else {
props.load(new InputStreamReader(is, "utf-8"));
}
} finally {
is.close();
}
return props;
}
}
2.在resource目录下创建目录META-INF,在META-INF目录下创建文件spring.factories
内容
org.springframework.boot.env.PropertySourceLoader=org.example.SelfPropertySourceLoader
重新运行
大功告成。。。
更多推荐
所有评论(0)