FreeMarker跨路径导入.ftl文件失败问题求助
问题描述
尝试用FreeMarker模板化Java类时,无法跨路径导入公共函数模板functions.ftl。配置了多个模板加载路径,但使用绝对路径/common/functions.ftl导入时提示找不到模板,使用相对路径又触发"不允许从根目录退回"的错误。多模块项目中需要共享公共模板,目前只能在每个模板目录放置副本才能正常使用。
当前配置Bean:
@Bean public FreeMarkerConfigurer freeMarkerConfigurer() { FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); String[] paths = Stream.of( "classpath:/templates", "classpath:/templates/dao/", "classpath:/templates/pom/", "classpath:/common/") .map(path -> path.replace("/", File.separator).replace("\\", File.separator)) .toArray(String[]::new); configurer.setTemplateLoaderPaths(paths); configurer.setDefaultEncoding("UTF-8"); configurer.setPostTemplateLoaders(); return configurer; }
模板导入代码(Type.ftl):
<#import "/common/functions.ftl" as function>
报错信息:
2024-02-27T14:55:44.552-06:00 ERROR 60233 --- [nio-8090-exec-1] freemarker.runtime : Error executing FreeMarker template freemarker.core._MiscTemplateException: Template importing failed (for parameter value "../../../common/functions.ftl"): Template not found for name "dao/udt/../../../common/functions.ftl". Reason given: Backing out from the root directory is not allowed. The name was interpreted by this TemplateLoader: MultiTemplateLoader(loader1 = FileTemplateLoader(baseDir="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/templates", canonicalBasePath="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/templates/"), loader2 = FileTemplateLoader(baseDir="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/templates/dao", canonicalBasePath="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/templates/dao/"), loader3 = FileTemplateLoader(baseDir="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/templates/pom", canonicalBasePath="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/templates/pom/"), loader4 = FileTemplateLoader(baseDir="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/common", canonicalBasePath="/Users/rahulverma/git/by-rahul/one-click-code/template-services-code-core/target/classes/common/"), loader5 = ClassTemplateLoader(resourceLoaderClass=org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer, basePackagePath="" /* relatively to resourceLoaderClass pkg */)).
解决方案
方案1:修改导入路径为无斜杠的文件名
FreeMarker的MultiTemplateLoader会按配置顺序遍历所有模板加载路径查找模板。将导入语句改为:
<#import "functions.ftl" as function>
这样FreeMarker会依次在templates、templates/dao、templates/pom、common目录下查找functions.ftl,最终在common目录找到目标文件。
方案2:调整模板加载路径配置
去掉子目录的单独配置,只保留根级路径,让FreeMarker通过完整路径访问模板:
@Bean public FreeMarkerConfigurer freeMarkerConfigurer() { FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); // 只保留根路径,子目录通过模板内的路径引用 String[] paths = Stream.of( "classpath:/", "classpath:/common/" ) .map(path -> path.replace("/", File.separator).replace("\\", File.separator)) .toArray(String[]::new); configurer.setTemplateLoaderPaths(paths); configurer.setDefaultEncoding("UTF-8"); return configurer; }
此时导入公共模板可以用绝对路径:
<#import "/common/functions.ftl" as function>
同时其他模板的引用也要调整为完整路径,比如/templates/dao/udt/Type.ftl。
方案3:将公共函数设为全局共享变量
通过代码提前加载公共模板中的函数,注入为FreeMarker的全局共享变量,无需在每个模板中导入:
@Bean public FreeMarkerConfigurer freeMarkerConfigurer() throws IOException, TemplateException { FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); String[] paths = Stream.of( "classpath:/templates", "classpath:/common/" ) .map(path -> path.replace("/", File.separator).replace("\\", File.separator)) .toArray(String[]::new); configurer.setTemplateLoaderPaths(paths); configurer.setDefaultEncoding("UTF-8"); // 加载公共函数模板并设置为全局变量 configurer.setPostTemplateLoaders(); Configuration configuration = configurer.createConfiguration(); Template functionTemplate = configuration.getTemplate("functions.ftl"); // 获取模板中的函数哈希表 Map<String, Object> functionMap = new HashMap<>(); functionTemplate.process(functionMap, new StringWriter()); // 将函数设置为全局共享变量 configuration.setSharedVariable("function", functionMap.get("function")); configurer.setConfiguration(configuration); return configurer; }
这样所有模板无需导入,直接使用function.camelToSnakeCase(...)即可调用公共函数。
内容的提问来源于stack exchange,提问作者Rahul verma
相关产品推荐
相关产品推荐

