java+数据模型策略_FreeMarker开发-数据模型

本文详细介绍了FreeMarker模板引擎的数据模型,它基于哈希表结构,支持字符串、Map、对象等多种数据类型。在Java代码中,数据通过ObjectWrapper转换为FreeMarker内部的TemplateModel接口实现。FreeMarker提供了两种基本的对象包装器:DEFAULT_WRAPPER和BEANS_WRAPPER,分别对应不同的包装策略。此外,文章还讲解了如何自定义方法和指令,包括实现TemplateMethodModelEx接口创建累加方法,以及实现TemplateDirectiveModel接口创建自定义循环指令。
摘要由CSDN通过智能技术生成

FreeMarker用于处理模板的数据模型是哈希表,也就是一个树状结构的name-value对。如下:

(root)

|

+- string="string"

|

+- map

| |

| +- map1 = "map1"

| |

| +- map2 = "map2"

|

+- object

| |

| +- field1= "field1"

| |

| +- field2 = "field1"

| |

| +- method= "method"|

......

在Java代码中(下面第15行),我们提供给模板引擎的数据(process方法的第一个参数),可以是Map,也可以是自定义的Java对象。但是,模板引擎在处理时,并不是直接使用我们提供的类型。它会将其转换为自己内部定义的类型,转换工作由第8行的ObjectWrapper去完成,这种特性被称作“对象包装(Object Swapping)”。

查看源码,关于Template#process(Object dataModel, Writer out)方法中dataModel参数的解释如下:

dataModel是从模板中可以获取的变量的容器(name-value对);

dataModel通常是一个Map或者是一个JavaBean(JavaBean的属性将成为变量);

可以是BeanWrapper在执行时可以转换为TemplateHashModel的任意对象;

也可以是实现了TemplateHashModel接口的对象,这种情况在执行时将直接使用,不再进行包装;

如果是null,则使用一个空的数据模型。

1 public classFreeMarkerTest {2 public static void main(String[] args) throwsIOException,3 TemplateException {4 Configuration conf = newConfiguration();5 //设置加载模板文件的目录

6 conf.setDirectoryForTemplateLoading(new File("src/templates"));7 //设置模板检索数据模型的方式

8 conf.setObjectWrapper(newDefaultObjectWrapper());9 //创建、解析模板并缓存

10 Template template = conf.getTemplate("example.flt");11 //准备数据

12 Maproot = new HashMap();13 root.put("example", "Hello World!");14 //将数据与模板合并

15 template.process(root, newOutputStreamWriter(System.out));16 }17 }

一、对象包装(Object Swapping)

FreeMarker数据模型的哈希表中的name为字符串,value可以为标量、容器、子程序和节点等。这也是FreeMarker内部使用的变量的类型。这些类型都实现了freemarker.template.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类型包装为freemarker.ext.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());

二、自定义方法

自定义方法需要实现freemarker.template.TemplateMethodModel接口(当前已@deprecated),建议替换为TemplateMethodModelEx。

例:实现累加方法sum(int...num)。参数可以有多个整数。

模板如下:

${sum(1, 2, 3, 4)}

代码如下:

packageorg.genein.freemark.templateModel;importjava.util.List;importfreemarker.template.SimpleNumber;importfreemarker.template.TemplateMethodModelEx;importfreemarker.template.TemplateModelException;public class SumMethod implementsTemplateMethodModelEx {

@SuppressWarnings("rawtypes")

@Overridepublic Object exec(List arg0) throwsTemplateModelException {if (arg0 == null || arg0.size() == 0) {return new SimpleNumber(0);

}double sum = 0l;doubletmp;for (int i = 0; i < arg0.size(); i++) {

tmp=Double.valueOf(arg0.get(i).toString());

sum+=tmp;

}return newSimpleNumber(sum);

}

}

FreeMarkerTest.main方法添加以下代码:

1 //添加方法工具

2 root.put("sum", newSumMethod());3 //将数据与模板合并

4 template.process(root, new OutputStreamWriter(System.out));

输出如下:

10

三、自定义指令

自定义指令需要实现freemarker.template.TemplateDirectiveModel接口。

例:自定义一个指令,循环输出内嵌内容,count参数决定循环次数(必填),hr参数决定是否添加分隔符“”(可选,默认false),step参数表示当前循环次数(可选)。

模板如下:

false; step>${step}. ${name}@repeat>

代码如下:

packageorg.genein.freemark.templateModel;importjava.io.IOException;importjava.util.Map;importfreemarker.core.Environment;importfreemarker.template.SimpleNumber;importfreemarker.template.TemplateBooleanModel;importfreemarker.template.TemplateDirectiveBody;importfreemarker.template.TemplateDirectiveModel;importfreemarker.template.TemplateException;importfreemarker.template.TemplateModel;importfreemarker.template.TemplateModelException;importfreemarker.template.TemplateNumberModel;public class RepeatDirective implementsTemplateDirectiveModel {/*** 循环次数*/

private static final String COUNT = "count";/*** 是否需要用hr标签间隔*/

private static final String HR = "hr";

@SuppressWarnings("rawtypes")

@Overridepublic voidexecute(Environment env, Map params, TemplateModel[] loopVars,

TemplateDirectiveBody body)throwsTemplateException, IOException {//获取count参数,并校验是否合法

TemplateModel countModel =(TemplateModel) params.get(COUNT);if (countModel == null) {throw new TemplateModelException("缺少必须参数count!");

}if (!(countModel instanceofTemplateNumberModel)) {throw new TemplateModelException("count参数必须为数值型!");

}int count =((TemplateNumberModel) countModel).getAsNumber().intValue();if (count < 0) {throw new TemplateModelException("count参数值必须为正整数!");

}//获取hr参数,并校验是否合法

boolean hr = false;

TemplateModel hrModel=(TemplateModel) params.get(HR);if (hrModel != null) {if (!(hrModel instanceofTemplateBooleanModel)) {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前面,因为内嵌内容中使用到了该循环变量。//body.render(new UpperCaseFilterWriter(env.getOut()));

body.render(env.getOut());if(hr) {

env.getOut().write("");

}

}

}

}

FreeMarkerTest.main增加以下代码:

root.put("name", "Genein");//添加自定义指令

root.put("repeat", newRepeatDirective());//将数据与模板合并

template.process(root, new OutputStreamWriter(System.out));

输出如下:

1. Genein

2. Genein

3. Genein

4. Genein

5. Genein

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值