FreeMarker能接收的数据类型(HashMap、java类)

转载 2018年04月15日 00:11:40

在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,则使用一个空的数据模型。

复制代码
 1 public class FreeMarkerTest {
 2     public 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 = conf.getTemplate("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内部使用的变量的类型。这些类型都实现了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实现
字符串TemplateScalarModelSimpleScalar
数值TemplateNumberModelSimpleNumber
日期TemplateDateModelSimpleDate
布尔TemplateBooleanModelTemplateBooleanModel.TRUE
哈希TemplateHashModelSimpleHash
序列TemplateSequenceModelSimpleSequence
集合TemplateCollectionModelSimpleCollection
节点TemplateNodeModelNodeModel

 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)}

代码如下:

复制代码
package org.genein.freemark.templateModel;

import java.util.List;

import freemarker.template.SimpleNumber;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.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.valueOf(arg0.get(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

 

、自定义指令

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

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

模板如下:

<@repeat count=5 hr=false; step>
${step}. ${name}
</@repeat>

代码如下:

复制代码
package org.genein.freemark.templateModel;

import java.io.IOException;
import java.util.Map;

import freemarker.core.Environment;
import freemarker.template.SimpleNumber;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.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) params.get(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) params.get(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前面,因为内嵌内容中使用到了该循环变量。
            // body.render(new UpperCaseFilterWriter(env.getOut()));
            body.render(env.getOut());
            if (hr) {
                env.getOut().write("<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

4、freemarker程序开发

1、程序开发入门 1.1 创建配置实例 首先,你应该创建一个freemarker.template.Configuration的实例,然后调整它的设置。Configuration实例是存储FreeM...
  • kaoa000
  • kaoa000
  • 2014-05-21 10:21:32
  • 5382

Freemarker与Spring设置自定义函数

以前经常用freemarker,但是从来没有系统的学习过,最近有时间了,找到资料学习一下,官方文档下载地址:打开下载 freemarker会用并不难,本篇博客是分享freemarker和spri...
  • fengyong7723131
  • fengyong7723131
  • 2017-01-22 16:28:20
  • 2705

FreeMarker2.3.10―Programmer's Guide(中文版)

前言 一、快速入门 1.1、创建配置实例 1.2、创建数据模型(Data Model) 1.3、获取模版(template) 1.4、把模版与数据模型合并 1.5、完整的代码 二、数据...
  • nengyu
  • nengyu
  • 2011-09-22 16:05:16
  • 3074

FreeMarker 自定义方法(二)

1. 模板文件 test303.ftl ${indexOf("met", x)} ${indexOf("foo", x)}2. 自定义方法 IndexOfMethod.javaps: 返回第一个字符串...
  • hanshileiai
  • hanshileiai
  • 2015-07-13 11:12:20
  • 2595

freemarker自定义函数

这里介绍freemarker的自定义函数实现对list的排序定义自定义函数的类创建一个普通类,并使之实现TemplateMethodModelEx接口,实现该接口的方法必须实现exec方法。在free...
  • wqh8522
  • wqh8522
  • 2017-06-04 09:19:10
  • 574

freemarker自定义指令及方法

“一节复一节,千枝攒万叶”竹子每生长一段就会总结一下打个节,今天我也总结一下最近处理的一个小问题,希望我的小节,对各位看官有所帮助。 背景描述: 最近项目做了一个月报功能,由于生产环境不是所有人都能访...
  • suijiarui
  • suijiarui
  • 2016-05-12 14:30:03
  • 4144

freemarker快速入门

一、快速入门 1.1、创建配置实例 首先你需要创建一个Configuration(freemarker.template.Configuration)的实例, 设置其中的某些属性。C...
  • zhuliliang
  • zhuliliang
  • 2017-07-03 16:53:03
  • 105

实习期间问题日志

实习两个半月,参与一个类似企业门户网站开发的项目。项目采用的云计算服务模式包括基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS);项目基于Spring Boot框架和SSM框架...
  • dlplaymaker
  • dlplaymaker
  • 2017-07-07 15:39:41
  • 190

Freemarker模板获取HashMap重新封装类的问题(自定义类)

如果Bean是或者其某个属性是MAP类型,在ftl模板中只能当作freemarker的哈希表使用,且map的key应该为字符串。 如果想将bean在freemarker模板中可以像Java中一样...
  • it_zhaonan
  • it_zhaonan
  • 2016-05-11 15:10:17
  • 484

关于freemarker里面的hashmap使用问题

The Example  is given:         Map testMap = new HashMap();         optionMap.put("sh", "shanghai ")...
  • jason_hisoft
  • jason_hisoft
  • 2007-10-26 14:56:00
  • 1785
收藏助手
不良信息举报
您举报文章:FreeMarker能接收的数据类型(HashMap、java类)
举报原因:
原因补充:

(最多只允许输入30个字)