FreeMarker能接收的数据类型(HashMap、java类)
在Java代码中(下⾯第15⾏),我们提供给模板引擎的数据(process⽅法的第⼀个参数),可以是Map,也可以是⾃定义的Java对象。但是,模板引擎在处理时,并不是直接使⽤我们提供的类型。它会将其转换为⾃⼰内部定义的类型,转换⼯作由第8⾏的ObjectWrapper去完成,这种特性被称作“对象包装(Object Swapping)”。
查看源码,关于Template#process(Object dataModel, Writer out)⽅法中dataModel参数的解释如下:
dataModel是从模板中可以获取的变量的容器(name-value对);
dataModel通常是⼀个Map<String, Object>或者是⼀个JavaBean(JavaBean的属性将成为变量);
可以是BeanWrapper在执⾏时可以转换为TemplateHashModel的任意对象;
也可以是实现了TemplateHashModel接⼝的对象,这种情况在执⾏时将直接使⽤,不再进⾏包装;
如果是null,则使⽤⼀个空的数据模型。
1public class FreeMarkerTest {
2public static void main(String[] args) throws IOException,
3 TemplateException {
4 Configuration conf = new Configuration();
5// 设置加载模板⽂件的⽬录
6 conf.setDirectoryForTemplateLoading(new File("src/templates"));
7// 设置模板检索数据模型的⽅式
8 conf.setObjectWrapper(new DefaultObjectWrapper());
9// 创建、解析模板并缓存
10 Template template = Template("example.flt");
11// 准备数据
12 Map<String, Object> root = new HashMap<String, Object>();
13 root.put("example", "Hello World!");
14// 将数据与模板合并
15 template.process(root, new OutputStreamWriter(System.out));
16 }
17 }
⼀、对象包装(Object Swapping)
FreeMarker数据模型的哈希表中的name为字符串,value可以为标量、容器、⼦程序和节点等。这也是FreeMarker内部使⽤的变量的类型。这些类型都实现了plate.TemplateModel接⼝。
1. 标量:包括数值、字符串、⽇期、布尔。
2. 容器:包括哈希表(Map,类似于Java中的Map)、序列(Sequence,类似于Java中的数组和有序集合)、集合(Collection,类似于Java中的集合)。
3. ⼦程序:包括⽅法(Method)和指令(Directive)。
4. 节点:主要是为了帮助⽤户在数据模型中处理XML⽂档。
在模板处理时,会将Java类型包装为对应的TemplateModel实现。⽐如将⼀个String包装为SimpleScalar来存储同样的值。对于每个Java类型,具体选择什么TemplateModel实现去包装,取决于对象包装器(ObjectWrapper)的实现策略,可通过上⾯代码第8⾏设置。ObjectWrapper是⼀个接⼝,FreeMarker核⼼包提供了两个基本的实现:ObjectWrapper.DEFAULT_WRAPPER、ObjectWrapper.BEANS_WRAPPER。
1. ObjectWrapper.DEFAULT_WRAPPER:按照下表对应关系包装,Jython类型包装为jython.JythonWrapper,其它类型调⽤BEANS_WRAPPER。
2. ObjectWrapper.BEANS_WRAPPER:利⽤Java反射来获取对象的成员属性。
类型FreeMarker接⼝FreeMarker实现
字符串TemplateScalarModel SimpleScalar
数值TemplateNumberModel SimpleNumber
⽇期TemplateDateModel SimpleDate
布尔TemplateBooleanModel TemplateBooleanModel.TRUE
哈希TemplateHashModel SimpleHash
序列TemplateSequenceModel SimpleSequence
集合TemplateCollectionModel SimpleCollection
节点TemplateNodeModel NodeModel
ObjectWrapper的这两个属性现已经被标注为@deprecated,并建议⽤DefaultObjectWrapperBuilder#build()和BeansWrapperBuilder#build()⽅式获取实例。如下:// conf.setObjectWrapper(new DefaultObjectWrapperBuilder(new Version("2.3.22")).build());
conf.setObjectWrapper(new BeansWrapperBuilder(new Version("2.3.22")).build());
⼆、⾃定义⽅法
⾃定义⽅法需要实现plate.TemplateMethodModel接⼝(当前已@deprecated),建议替换为TemplateMethodModelEx。
例:实现累加⽅法um)。参数可以有多个整数。
模板如下:
${sum(1, 2, 3, 4)}
代码如下:
plateModel;
import java.util.List;
parameter数据类型plate.SimpleNumber;
plate.TemplateMethodModelEx;
plate.TemplateModelException;
public class SumMethod implements TemplateMethodModelEx {
@SuppressWarnings("rawtypes")
@Override
public Object exec(List arg0) throws TemplateModelException {
if (arg0 == null || arg0.size() == 0) {
return new SimpleNumber(0);
}
double sum = 0l;
double tmp;
for (int i = 0; i < arg0.size(); i++) {
tmp = Double.(i).toString());
sum += tmp;
}
return new SimpleNumber(sum);
}
}
FreeMarkerTest.main⽅法添加以下代码:
1// 添加⽅法⼯具
2 root.put("sum", new SumMethod());
3// 将数据与模板合并
4 template.process(root, new OutputStreamWriter(System.out));
输出如下:
10
三、⾃定义指令
⾃定义指令需要实现plate.TemplateDirectiveModel接⼝。
例:⾃定义⼀个指令,循环输出内嵌内容,count参数决定循环次数(必填),hr参数决定是否添加分隔符“<hr>”(可选,默认false),step参数表⽰当前循环次数(可选)。
模板如下:
<@repeat count=5 hr=false; step>
${step}. ${name}
</@repeat>
代码如下:
plateModel;
import java.io.IOException;
import java.util.Map;
Environment;
plate.SimpleNumber;
plate.TemplateBooleanModel;
plate.TemplateDirectiveBody;
plate.TemplateDirectiveModel;
plate.TemplateException;
plate.TemplateModel;
plate.TemplateModelException;
plate.TemplateNumberModel;
public class RepeatDirective implements TemplateDirectiveModel {
/**
* 循环次数
*/
private static final String COUNT = "count";
/**
* 是否需要⽤hr标签间隔
*/
private static final String HR = "hr";
@SuppressWarnings("rawtypes")
@Override
public void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException {
// 获取count参数,并校验是否合法
TemplateModel countModel = (TemplateModel) (COUNT);
if (countModel == null) {
throw new TemplateModelException("缺少必须参数count!");
}
if (!(countModel instanceof TemplateNumberModel)) {
throw new TemplateModelException("count参数必须为数值型!");
}
int count = ((TemplateNumberModel) countModel).getAsNumber().intValue();
if (count < 0) {
throw new TemplateModelException("count参数值必须为正整数!");
}
/
/ 获取hr参数,并校验是否合法
boolean hr = false;
TemplateModel hrModel = (TemplateModel) (HR);
if (hrModel != null) {
if (!(hrModel instanceof TemplateBooleanModel)) {
throw new TemplateModelException("hr参数值必须为布尔型!");
}
hr = ((TemplateBooleanModel) hrModel).getAsBoolean();
}
// 检验内嵌内容是否为空
if (body == null) {
throw new RuntimeException("内嵌内容不能为空!");
}
}
// 最多只允许⼀个循环变量
if (loopVars.length > 1) {
throw new TemplateModelException("最多只允许⼀个循环变量!");
}
// 循环渲染内嵌内容
for (int i = 0; i < count; i++) {
// ⽤第⼀个循环变量记录循环次数
if (loopVars.length == 1) {
loopVars[0] = new SimpleNumber(i + 1);
}
// 上⾯设置循环变量的操作必须在该render前⾯,因为内嵌内容中使⽤到了该循环变量。 // der(new Out()));
if (hr) {
}
}
}
}
FreeMarkerTest.main增加以下代码:
root.put("name", "Genein");
// 添加⾃定义指令
root.put("repeat", new RepeatDirective());
// 将数据与模板合并
template.process(root, new OutputStreamWriter(System.out));
输出如下:
1. Genein
2. Genein
3. Genein
4. Genein
5. Genein
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论