设置Freemarker默认编码为UTF-8为何导致模板读取异常?
问题背景
基于Gradle KTS的Spring Boot项目,使用独立Freemarker库implementation("org.freemarker:freemarker:2.3.32")生成文本模板,核心代码如下:
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32); cfg.setDirectoryForTemplateLoading(new ClassPathResource("templates").getFile()); cfg.setDefaultEncoding("utf-8"); cfg.setOutputEncoding("utf-8"); Template template = cfg.getTemplate("template.ftl"); System.out.println("template=" + template); Map<String, Object> data = new HashMap<>(); data.put("vehicles", List.of(new Vehicle("auto", 60), new Vehicle("koloběžka", 5))); StringWriter stringWriter = new StringWriter(); template.process(data, stringWriter); String result = stringWriter.toString(); System.out.println(result);
测试用模板文件template.ftl内容为:
jůůů
模板文件和传入数据均包含捷克字符,且IntelliJ显示模板为UTF-8编码。
异常现象
- 不设置
Configuration的默认编码时,Freemarker使用本地编码(推测为Windows-1250),模板能正确打印含变音符号的内容; - 显式设置默认编码为UTF-8后,打印结果变为乱码
j���。
疑问
- 为何模板与配置均为UTF-8时无法正确读取变音符号?
- 为何默认Windows-1250编码能正常读取UTF-8编码的模板?
原因分析
1. 无编码配置时正常的巧合
捷克字符ů在UTF-8编码中是两个字节C5 AF,而这两个字节恰好与Windows-1250编码中ů的字节值完全一致。因此当Freemarker用Windows-1250读取UTF-8编码的模板文件时,刚好能解析出正确的字符,这属于编码字节重叠的巧合,并非正常兼容逻辑。
2. 设置UTF-8后乱码的核心原因
问题出在Gradle构建过程的资源处理和模板加载方式:
- Gradle默认会以系统编码(Windows下为Windows-1250)复制
src/main/resources下的资源文件。即使你在IntelliJ中把模板设为UTF-8,Gradle构建时会将其转码为Windows-1250后输出到构建目录; - 此时用UTF-8编码读取转码后的文件,自然会出现乱码。而无编码配置时用Windows-1250读取转码后的文件,刚好匹配,所以显示正常。
- 另外,
ClassPathResource("templates").getFile()的加载方式存在隐患:当项目打包为JAR后,类路径内的资源无法通过File对象直接访问,会抛出异常。
解决方法
方法一:强制Gradle以UTF-8处理模板资源
在build.gradle.kts中添加配置,确保Freemarker模板文件以UTF-8编码复制,避免转码:
tasks.withType<ProcessResources> { filteringCharset = "UTF-8" filesMatching("**/*.ftl") { options.encoding = "UTF-8" } }
方法二:改用类路径加载器读取模板(推荐)
放弃File方式加载,改用Freemarker的ClassTemplateLoader直接从类路径读取资源,这样既避免JAR打包后的访问问题,也能确保编码读取正确:
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32); // 从当前类的类路径下加载/templates目录中的模板 cfg.setTemplateLoader(new ClassTemplateLoader(getClass(), "/templates")); cfg.setDefaultEncoding("utf-8"); cfg.setOutputEncoding("utf-8");
内容的提问来源于stack exchange,提问作者milkamar
相关产品推荐
相关产品推荐

