一、创建 Configuration 实例
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
cfg .setClassForTemplateLoading(this.getClass(), "ftl");
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
不需要重复创建 Configuration
实例; 它的代价很高,尤其是会丢失缓存。Configuration
实例就是应用级别的单例。
二、获取模板
Template temp = cfg.getTemplate("hashes.ftl");
Configuration
缓存 Template
实例,当再次获得 hashes.ftl
的时候,它可能再读取和解析模板文件了, 而只是返回第一次的 Template
实例。
三、数据模型
在内部,模板中可用的变量都是实现了freemarker.template.TemplateModel
接口的Java对象。 但在数据模型中,可以使用基本的Java集合类作为变量,因为这些变量会在内部被替换为适当的 TemplateModel
类型,类freemarker.template.ObjectWrapper的职责
A、标量
有4种类型的标量:
- 布尔值
- 数字
- 字符串
- 日期类型(子类型: 日期(没有时间部分),时间或者日期-时间)
每一种标量类型都是 TemplateTypeModel
接口的实现,这里的 Type
就是类型的名称。这些接口只定义了一个方法: typegetAsType();
。 它返回变量的Java类型(boolean
, Number
, String
和 Date
各自代表的值)。
B、哈希表 对应TemplateHashModel
C、序列对应TemplateSequenceModel
D、集合 对应TemplateCollectionModel
E、方法对应TemplateMethodModel
方法示例:
public class IndexOfMethod implements TemplateMethodModel { public TemplateModel exec(List args) throws TemplateModelException { if (args.size() != 2) { throw new TemplateModelException("Wrong arguments"); } return new SimpleNumber( ((String) args.get(1)).indexOf((String) args.get(0))); } }
如果将一个实例放入根数据模型中,像这样:
root.put("indexOf", new IndexOfMethod());
那么就可以在模板中调用:
<#assign x = "something"> ${indexOf("met", x)} ${indexOf("foo", x)}
将会输出:
2 -1
F、指令 对应TemplateDirectiveModel
我们来创建一个指令,这个指令可以一次又一次地执行其中的嵌套内容, 这个次数由指定的数字来确定(就像 list
指令), 可以使用<hr>
将输出的重复内容分开。 这个指令我们命名为"repeat"。示例模板如下:
<#assign x = 1> <@repeat count=4> Test ${x} <#assign x++> </@repeat> <@repeat count=3 hr=true> Test </@repeat> <@repeat count=3; cnt> ${cnt}. Test </@repeat>
输出为:
Test 1 Test 2 Test 3 Test 4 Test <hr> Test <hr> Test 1. Test 2. Test 3. Test指令的实现类为:
public class RepeatDirective implements TemplateDirectiveModel {
private static final String PARAM_NAME_COUNT = "count";
private static final String PARAM_NAME_HR = "hr";
public void execute(Environment env,
Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body)
throws TemplateException, IOException {
// ---------------------------------------------------------------------
// Processing the parameters:
int countParam = 0;
boolean countParamSet = false;
boolean hrParam = false;
Iterator paramIter = params.entrySet().iterator();
while (paramIter.hasNext()) {
Map.Entry ent = (Map.Entry) paramIter.next();
String paramName = (String) ent.getKey();
TemplateModel paramValue = (TemplateModel) ent.getValue();
if (paramName.equals(PARAM_NAME_COUNT)) {
if (!(paramValue instanceof TemplateNumberModel)) {
throw new TemplateModelException(
"The \"" + PARAM_NAME_HR + "\" parameter "
+ "must be a number.");
}
countParam = ((TemplateNumberModel) paramValue)
.getAsNumber().intValue();
countParamSet = true;
if (countParam < 0) {
throw new TemplateModelException(
"The \"" + PARAM_NAME_HR + "\" parameter "
+ "can't be negative.");
}
} else if (paramName.equals(PARAM_NAME_HR)) {
if (!(paramValue instanceof TemplateBooleanModel)) {
throw new TemplateModelException(
"The \"" + PARAM_NAME_HR + "\" parameter "
+ "must be a boolean.");
}
hrParam = ((TemplateBooleanModel) paramValue)
.getAsBoolean();
} else {
throw new TemplateModelException(
"Unsupported parameter: " + paramName);
}
}
if (!countParamSet) {
throw new TemplateModelException(
"The required \"" + PARAM_NAME_COUNT + "\" paramter"
+ "is missing.");
}
if (loopVars.length > 1) {
throw new TemplateModelException(
"At most one loop variable is allowed.");
}
// Yeah, it was long and boring...
// ---------------------------------------------------------------------
// Do the actual directive execution:
Writer out = env.getOut();
if (body != null) {
for (int i = 0; i < countParam; i++) {
// Prints a <hr> between all repetations if the "hr" parameter
// was true:
if (hrParam && i != 0) {
out.write("<hr>");
}
// Set the loop variable, if there is one:
if (loopVars.length > 0) {
loopVars[0] = new SimpleNumber(i + 1);
}
// Executes the nested body (same as <#nested> in FTL). In this
// case we don't provide a special writer as the parameter:
body.render(env.getOut());
}
}
}
}
四、实用类
BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
TemplateHashModel staticModels = wrapper.getStaticModels();
TemplateHashModel fileStatics =
(TemplateHashModel) staticModels.get("java.io.File");
2、访问枚举类型
BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
TemplateHashModel enumModels = wrapper.getEnumModels();
TemplateHashModel roundingModeEnums =
(TemplateHashModel) enumModels.get("java.math.RoundingMode");
3、自定义模版加载
FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates"));
FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates"));
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "");
TemplateLoader[] loaders = new TemplateLoader[] { ftl1, ftl2, ctl };
MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);
cfg.setTemplateLoader(mtl);