什么是 FreeMarker
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本( HTML 网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个 Java 类库,是一款程序员可以嵌入他们所开发产品的组件。简单说,
FreeMarker: 模板 + 数据 = 输出
FreeMarker 内建指令 —— include
FreeMarker 中包含很多内建的指令,犹如 JSP 中的标签一样。这里主要讲下 include
指令。参考 Freemarker 官方文档,其语法格式如下:
<#include path> 或 <#include path options>
其中:
path
: 要包含文件的路径;一个算作是字符串的表达式。(用其他话说, 它不用是一个固定的字符串,它也可以是像 profile.baseDir + "/menu.ftl" 这样的东西。)options
: 一个或多个这样的选项: encoding=encoding, parse=parseencoding
: 算作是字符串的表达式parse
: 算作是布尔值的表达式(为了向下兼容,也接受一部分字符串值)ignore_missing
: 算作是布尔值的表达式
你可以使用它在你的模板中插入另外一个 FreeMarker 模板文件 (由 path
参数指定)。 被包含模板的输出格式是在 include
标签出现的位置插入的。被包含的文件和包含它的模板共享变量,就像是被复制粘贴进去的一样。值得注意的是,include
指令不能由被包含文件的内容所替代,它只是当 FreeMarker 每次在模板处理期间到达 include 指令时处理被包含的文件。也就是说,这边可以在执行时,动态指定需包含的 FreeMarker 模板文件。
一个错误场景
假设有这样一个场景,需要介绍全国的每一个省。每个省有一个对应的以省份名称命名的 FreeMarker 模板(当然,这些模板都是在一个总的模板下显示),如:js.ftl。
一般来说,我们可以这样写总的模板,来达到动态包含省份的模板。
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<#include "/${province}.ftl"/>
</body>
</html>
看着上面的代码,是不是觉得 FreeMarker 还是很方便很强大的啊!但是坑来了,假设目前项目还没有开发完成,也就是说,有的省份还没有对应的 FreeMarker 模板,那么会出现什么情况呢?直接报错了。对于项目没做完这种事,我们得找个临时方案啊,给那些没有开发模板的省份来个默认页面,那么用 include
指令貌似有点力不从心。如何解决呢?FreeMarker 有自定义指令的功能,那就定义一个加强版的 include
指令吧,使得该指令在找不到指定模板的情况下,包含一个默认的模板。
public class IncludeXMacro implements TemplateDirectiveModel {
private static final String PATH_PARAM = "template";
private static final String DEFALUT_PATH_PARAM = "default_template";
@Override
public void execute(Environment environment, @SuppressWarnings("rawtypes") Map params, TemplateModel[] templateModel,
TemplateDirectiveBody directiveBody) throws TemplateException, IOException {
TemplateLoader templateLoader = environment.getConfiguration().getTemplateLoader();
String fullTemplatePath = getFullTemplatePath(environment, params, PATH_PARAM);
if (templateLoader.findTemplateSource(fullTemplatePath) != null) {
environment.include(environment.getTemplateForInclusion(fullTemplatePath, null, true));
} else {
String defaultFullTemplatePath = getFullTemplatePath(environment, params, DEFALUT_PATH_PARAM);
if (templateLoader.findTemplateSource(defaultFullTemplatePath) == null) {
throw new _MiscTemplateException(environment, "Missing template file path:" + defaultFullTemplatePath);
}
environment.include(environment.getTemplateForInclusion(defaultFullTemplatePath, null, true));
}
}
/**
* Description:获取ftl完整路径 <br>
*/
private String getFullTemplatePath(Environment environment, @SuppressWarnings("rawtypes") Map params, String templatePath)
throws MalformedTemplateNameException {
if (!params.containsKey(templatePath)) {
throw new MalformedTemplateNameException("missing required parameter '" + templatePath, "'");
}
String currentTemplateName = environment.getTemplate().getName();
final String baseName = FilenameUtils.getPath(currentTemplateName);
final String targetName = params.get(templatePath).toString();
final String fullTemplatePath = environment.toFullTemplateName(baseName, targetName);
return fullTemplatePath;
}
}
FreeMarker 在这边就不详细讲了,官网讲的很清楚。我们给这个自定义的指令起个名字:includeX
,然后就可以这样使用了:
<@includeX template="/${province}.ftl" default_template="/default.ftl"/>