Freemarker 程序支付

 

今天web开发被,多利用freemarker
来讲述页面。通常会采用的macro来定义各种零件,从而达成UI组件的复用。结合使用其它的指定,可高效的讲述一个html页面。那么是否拿freemarker用在另地方也?又是什么将freemarker用到任何地方吧。本篇文章就来介绍一下freemarker的用法。

 

         首先应第一独问题,是得能用到外地方的。

有关第二单问题,则于羁押了脚的情后,答案自明。

 

Freemarker 程序开发

  • 1、入门
  • 2、数据模型
    • 2.1
      基本内容
    • 2.2
      标量类型
    • 2.3
      容器类
    • 2.4
      方法
    • 2.5
      指令
    • 2.6
      对象包装
  • 3、配置
    • 共享变量
    • 布局安装
    • 模板加载
      • 模板加载器
      • 模板缓存

 

 

1、入门:

 

先是,你该创建一个 freemarker.template.Configuration 实例,
然后调其的安装。Configuration 实例是储存 FreeMarker
应用级设置的主导组成部分。同时,它吗处理创建同 缓存 预解析模板(比如 Template 对象)的工作。

或许你唯有当应用(可能是servlet)生命周期的开始尽同一不好

一个Configuration创建工厂:

 

package com.fjn.helper.frameworkex.freemarker;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

import freemarker.template.Configuration;

import freemarker.template.TemplateExceptionHandler;

 

public class ConfigurationFactory {

    private Configuration config;

    private static final Logger logger = LoggerFactory.getLogger(ConfigurationFactory.class);

    private Object initLock = new Object();

 

    public Configuration getConfig() {

       init();

       return config;

    }

 

    private void init() {

       if (config != null) {

           return;

       }

       synchronized (initLock) {

           if (config != null) {

              return;

           }

           Configuration config = new Configuration();

           try {

              // File rootClassPath = new

              // File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());

              // config.setDirectoryForTemplateLoading(new File(rootClassPath,

              // "/ftls"));

              config.setClassForTemplateLoading(ConfigurationFactory.class, "/ftls");

 

              config.setDefaultEncoding("UTF-8");

              config.setDateFormat("yyyy-MM-dd");

              config.setNumberFormat("#.##");

              config.setClassicCompatible(true);

               config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

              config.setTemplateUpdateDelay(10);

 

              this.config = config;

           } catch (Exception ex) {

              logger.error(ex.getMessage(), ex);

           }

       }

    }

}

 

 

当简要的以身作则中若得下 java.lang 和 java.util 包中的切近,
还出用户从定义的Java Bean来构建数据对象:

  • 使用 java.lang.String 来构建字符串。
  • 使用 java.lang.Number 来派生数字型。
  • 使用 java.lang.Boolean 来构建布尔值。
  • 使用 java.util.List 或Java数组来构建序列。
  • 使用 java.util.Map 来构建哈希表。
  • 运自定义的bean类来构建哈希表,bean中之起和bean的性对应。比如, product 的 price 属性
    (getProperty())可以通过product.price 获取。(bean的action也堪透过这种方式将到;

 

package com.fjn.helper.frameworkex.freemarker;

 

public class Product {

    private String url;

    private String name;

   

    public String getUrl() {

       return url;

    }

    public void setUrl(String url) {

       this.url = url;

    }

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

}

 

 

 

package com.fjn.helper.frameworkex.freemarker;

 

import java.io.IOException;

import java.io.OutputStreamWriter;

import java.io.Writer;

import java.util.HashMap;

import java.util.Map;

 

import org.junit.Test;

 

import freemarker.template.Template;

import freemarker.template.TemplateException;

 

public class ProductTest {

         private ConfigurationFactory factory = new ConfigurationFactory();

 

         @Test

         public void test() throws TemplateException, IOException {

                   // Create the root hash

                   Map<String, Object> root = new HashMap<>();

                   // Put string “user” into the root

                   root.put("user", "Big Joe");

                   // Create the hash for “latestProduct”

                   Map<String, Object> latest = new HashMap<>();

                   // and put it into the root

                   root.put("latestProduct", latest);

                   // put “url” and “name” into latest

                   latest.put("url", "products/greenmouse.html");

                   latest.put("name", "green mouse");

 

                   // get a template

                   Template template = factory.getConfig().getTemplate("product.ftl");

 

                   Writer out = new OutputStreamWriter(System.out);

                   template.process(root, out);

                   out.flush();

                   System.out.println("over……");

         }

}

 

Java I/O
相关注意事项:基于 out 对象,必须管 out.close() 最后给调用。当 out 对象为辟并将模板的出口写副文件时,这是不行电影的做法。其它时候,
比如典型的Web应用程序,那即便 不能 关闭 out 对象。

 

 

小结一下利用freemarker开发顺序的流水线:

1、创建一个configuration ,并召开有配备。

2、开发模板文件(.ftl)

3、为各一个模板指定相应的数据模型(map)。

4、使用configuration取得模板并拓展处理生成最终的文书。

 

 

2、数据模型

 

2.1 基本内容:

在 入门 章节中,
我们既了解怎么样采取基本的Java类(MapString,等)来构建数据模型了。在里边,模板被可用的变量都是实现了freemarker.template.TemplateModel 接口的Java对象。
但在数据模型中,可以利用基本的Java集合类作为变量,因为这些变量会当内部被替换为宜的 TemplateModel 类型。这种效益特色深受名是 对象包装。对象包装功能可以透明地把 任何 类型的目标转换为实现了TemplateModel 接口类型的实例。这即叫下面的易成为可能,如以模板被把 java.sql.ResultSet 转换为序列变量,
javax.servlet.ServletRequest 对象转换成包含呼吁属性的哈希表变量,
甚至可遍历XML文档作为FTL变量(参考这里)。包装(转换)这些目标,
需要利用方便的,也就算是所谓的 对象包装器 实现(可能是打定义之落实);
这将于后面座谈。
现在底要点是想念由沙盘访问任何对象,它们必然且使转移为实现了 TemplateModel 接口的目标。那么首先你应有熟悉来描写 TemplateModel 接口的贯彻类似。

来一个简单的 freemarker.template.TemplateModel 子接口对应每种基本变量类型(TemplateHashModel 对诺哈希表,TemplateSequenceModel 对许序列, TemplateNumberModel 对承诺数字等等)。比如,想为模板下 java.sql.ResultSet 变量作为一个行,那么即便用编制一个 TemplateSequenceModel 的实现类似,这个类要能够读取 java.sql.ResultSet 中之始末。我们常这样说,使用 TemplateModel 的贯彻类 包装 了 java.sql.ResultSet,基本上只是封装 java.sql.ResultSet 来提供利用普通的TemplateSequenceModel 接口走访它。请小心一个好像可实现多独 TemplateModel 接口;这即是怎FTL变量可以生多种类型
(参看模板开发指南/数值,类型/基本内容)

告小心,这些接口的一个细微的贯彻是同 freemarker.template 包一起提供的。
例如,将一个 String 转换成FTL的字符串变量,
可以应用 SimpleScalar,将 java.util.Map 转换成FTL的哈希表变量,可以运用 SimpleHash 等等。

如若想尝试自己的 TemplateModel 实现,
一个略的法子是创建它的实例,然后用这实例放入数据模型中
(也便是拿它们 放到 哈希表的根root上)。
对象包装器将会晤吃模板提供其的原生态,因为其既落实了 TemplateModel 接口,所以无更换(包装)的需。
(这个技能当您无思量用对象包装器来包装(转换)某些对象时仍有效。)

 

2.2 标量类型:

发生4种植档次的标量:

  • 布尔值
  • 数字
  • 字符串
  • 日子类型(子类型: 日期(没有工夫有),时间还是日期-时间)

各个一样栽标量类型且是 TemplateTypeModel 接口的落实,这里的 Type 就是种的称谓。这些接口就定义了一个术: typegetAsType();
它回到变量的Java类型(boolean, Number, String 和 Date 各自代表的价值)。

Note:

鉴于历史遗留的由来,字符串标量的接口是 TemplateScalarModel,而不是TemplateStringModel
(因为前期的 FreeMarker 字符串就是标量。)

这些接口的一个微小的贯彻和 SimpleType 类名在 freemarker.template 包中凡可用的。
但是却并未 SimpleBooleanModel 类型;为了表示布尔值,
可以下 TemplateBooleanModel.TRUE 和 TemplateBooleanModel.FALSE 来单独采取。

Note:

由历史遗留的因由,字符串标量的兑现类似是 SimpleScalar,而不是 SimpleString

在FTL中标量是板上钉钉的。当在模板被装置变量的值时,
使用任何的实例来替换 TemplateTypeModel 实例时,
是不用转原来实例中贮存的价值的。

2.2.1 “日期” 类型的难

对于日期类型来说,有部分难题,因为Java
API通常不区分 java.util.Date 只存储日期有(April 4, 2003),
时间部分(10:19:18 PM),或双方都包藏(April 4, 2003 10:19:18 PM)。
为了用本文正确显示值(或者拓展其他确定的操作),FreeMarker必须懂得 java.util.Date 的哪个部分存储了发含义上的信息,
哪部分从没为用(通常是标志为0的)。不幸之凡,
通常该信息只是当值从数据库中获取时可用,
因为多数数据库来独立的日子,时间跟日期-时间(又称为日穿)类型, java.sql 有3单照应的 java.util.Date 子类和她相互配合。

TemplateDateModel 接口有少独措施:分别是 java.util.Date getAsDate() 和 int getDateType()
该接口典型的兑现是储存一个 java.util.Date 对象,
加上一个平头来分辨子类型。这个平头的值吗必须是 TemplateDateModel 接口中的常量之一:DATETIME, DATETIME 和 UNKNOWN

关于 UNKNOWNjava.lang 和 java.util 下的好像常见给自动转换成 TemplateModel 的兑现类似,就是所谓的 对象包装器``ObjectWrapper(请参考前的目标包装介绍)。
如果目标包装器要卷入 java.util.Date 类,
它不是 java.sql 日期类的实例,那就未克决定子类型是呀,
所以使用 UNKNOWN。之后,如果模板需要动用这个变量,
而且操作也亟需子类型,那便会见停执行并丢掉来荒唐。为了避免这种景象的发生,
对于那些或出题目的变量,模板开发人员必须旗帜鲜明地指定子类型,使用内建函数 date, time 或 datetime (比如lastUpdated?datetime)。请小心,
如果和格式化参数一起行使内建函数 string
比如foo?string(“MM/dd/yyyy”),那么 FreeMarker 就不要知道子类型了。

 

2.3 容器类:

容器包括哈希表,序列以及聚合三栽类型。

2.3.1哈希表

哈希表是落实了 TemplateHashModel 接口的Java对象。TemplateHashModel 有半点单主意: TemplateModel get(String key),这个法根据加的称返回子变量, boolean isEmpty(),这个艺术标明哈希表是否含有子变量。 get 方法当在加以的名称没有找到子变量时返回null。

TemplateHashModelEx 接口扩展了 TemplateHashModel。它多了重多的艺术,使得可以应用内建函数 values 和 keys 来枚举哈希表中的子变量。

时以的落实类似是 SimpleHash,该类实现了 TemplateHashModelEx 接口。从中间来说,它利用一个 java.util.Hash 类型的目标存储子变量。 SimpleHash 类的章程可以长和移除子变量。
这些措施应该为此来以变量被创造之后直接初始化。

在FTL中,容器是雷打不动的。那就是说你无能够长,替换和移除容器中的子变量。

2.3.2序列

队是落实了 TemplateSequenceModel 接口的Java对象。它包含两独法子:TemplateModel get(int index) 和 int size()

常采取的实现类似是 SimpleSequence。该类内部采用一个 java.util.List 类型的目标存储它们的子变量。 SimpleSequence 有补加子元素的方。
在排创建之后应该下这些方式来填充序列。

2.3.3集合

聚是实现了 TemplateCollectionModel 接口的Java对象。这个接口定义了一个智: TemplateModelIterator iterator()TemplateModelIterator 接口和 java.util.Iterator 相似,但是它们回到 TemplateModels 而不是 Object
而且它亦可抛出TemplateModelException 异常。

寻常采取的兑现类似是 SimpleCollection

 

 

 

 

2.4方法

 

方变量在存于实现了 TemplateMethodModel 接口的模板被。这个接口包含一个办法: TemplateModel
exec(java.util.List arguments)。
当使用 计调用表达式 调用智时,exec 方法将会见于调用。
形参将会包含FTL方法调用形参的价。exec 方法的回来值为闹了FTL方法调用表达式的归值。

TemplateMethodModelEx 接口扩展了 TemplateMethodModel 接口。它并未添加任何新办法。
事实上这目标实现这个 标记 接口是深受FTL引擎暗示,
形式参数应该一直盖 TemplateModel 的款型加大上 java.util.List。否则用会见盖 String 形式放入list。

鉴于这些好醒目的缘由,这些接口没有默认的实现。

比如说:下面是方式,返回第一单字符串在第二单字符串第一蹩脚面世常常之目位置,
如果第二独字符串中未包含第一个字符串,则归-1:

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

 

 

2.5 指令

 

Java程序员好下 TemplateDirectiveModel 接口在Java代码中贯彻自定义指令。详情可与API文档。

Note:

TemplateDirectiveModel 在 FreeMarker 2.3.11 版本时才投入,
来代替快被撇下之TemplateTransformModel

示例 1

我们若兑现一个命,
这个命令可以以在它初始标签和收标签中的字符都转移为那个写形式。
就像是模板:

foo

<@upper>

  bar

  <#-- All kind of FTL is allowed here -->

  <#list ["red", "green", "blue"] as color>

    ${color}

  </#list>

  baaz

</@upper>

wombat

以见面输出:

foo

  BAR

    RED

    GREEN

    BLUE

  BAAZ

wombat

下面是凭令类的源代码:

package com.example;

import java.io.IOException;

import java.io.Writer;

import java.util.Map;

 

import freemarker.core.Environment;

import freemarker.template.TemplateDirectiveBody;

import freemarker.template.TemplateDirectiveModel;

import freemarker.template.TemplateException;

import freemarker.template.TemplateModel;

import freemarker.template.TemplateModelException;

 

/**

 *  FreeMarker user-defined directive that progressively transforms

 *  the output of its nested content to upper-case.

 *  

 *  

 *  <p><b>Directive info</b></p>

 * 

 *  <p>Directive parameters: None

 *  <p>Loop variables: None

 *  <p>Directive nested content: Yes

 */

public class UpperDirective implements TemplateDirectiveModel {

    

    public void execute(Environment env,

            Map params, TemplateModel[] loopVars,

            TemplateDirectiveBody body)

            throws TemplateException, IOException {

        // Check if no parameters were given:

        if (!params.isEmpty()) {

            throw new TemplateModelException(

                    "This directive doesn't allow parameters.");

        }

        if (loopVars.length != 0) {

                throw new TemplateModelException(

                    "This directive doesn't allow loop variables.");

        }

        

        // If there is non-empty nested content:

        if (body != null) {

            // Executes the nested body. Same as <#nested> in FTL, except

            // that we use our own writer instead of the current output writer.

            body.render(new UpperCaseFilterWriter(env.getOut()));

        } else {

            throw new RuntimeException("missing body");

        }

    }

    

    /**

     * A {@link Writer} that transforms the character stream to upper case

     * and forwards it to another {@link Writer}.

     */ 

    private static class UpperCaseFilterWriter extends Writer {

       

        private final Writer out;

           

        UpperCaseFilterWriter (Writer out) {

            this.out = out;

        }

 

        public void write(char[] cbuf, int off, int len)

                throws IOException {

            char[] transformedCbuf = new char[len];

            for (int i = 0; i < len; i++) {

                transformedCbuf[i] = Character.toUpperCase(cbuf[i + off]);

            }

            out.write(transformedCbuf);

        }

 

        public void flush() throws IOException {

            out.flush();

        }

 

        public void close() throws IOException {

            out.close();

        }

    }

 

}

兹我们需要创造是仿佛的实例,
然后被这个命令在模板被得透过名称”upper”来访问
(或者是其他我们怀念用底名字)。一个可行之方案是管这命令放到数据模型中:

root.put("upper", new com.example.UpperDirective());

唯独再也好的做法是以常用之命作为 共享变量 放到 Configuration 中。

自然为足以利用 内建函数new 将指令放到一个FTL库(宏的集合,就像以模板被,
使用 include 或 import )中:

<#-- Maybe you have directives that you have implemented in FTL -->

<#macro something>

  ...

</#macro>

 

<#-- Now you can't use <#macro upper>, but instead you can: -->

<#assign upper = "com.example.UpperDirective"?new()>

 

 

 

 

2.6  对象包装

 

目标包装器是实现了 freemarker.template.ObjectWrapper 接口的类似。它的目标是促成Java对象(应用程序中特定类等,比如 StringMapList 实例)和FTL类型系统间的映照。换句话说,
它指定了模版如何当数据模型(包含从沙盘被调用的Java方法的返回值)中发觉Java对象。
对象包装器作为插件放入 Configuration 中,可以以 object_wrapper 属性设置
(或者采用Configuration.setObjectWrapper)。

起技术角度来说,FTL类型系统由事先介绍过的 TemplateModel 子接口
(TemplateScalarModelTemplateHashModeTemplateSequenceModel齐)来表示。要映射Java对象及FTL类型系统中,
对象包装器的 TemplateModel wrap(java.lang.Object obj) 方法会被调用。

偶尔FreeMarker需要重返映射,此时 对象包装器``ObjectWrapper 的 Object unwrap(TemplateModel) 方法就是受调用了
(或另的变迁,请参考API文档来博取详细内容)。最后的操作是以 ObjectWrapperAndUnwrapper 中,它是 ObjectWrapper 的子接口。很多事实上的包装器会实现 ObjectWrapperAndUnwrapper 接口。

咱们来拘禁一下包装Java对象并带有其他对象 (比如 MapList,数组,
或者有JavaBean属性的靶子)是怎么样开展的。可以这样说,对象包装器将 Object[] 数组包装成 TemplateSquenceModel 接口的组成部分落实。当FreeMarker需要FTL序列中项的时光,它见面调用TemplateSquenceModel.get(int index) 方法。该法的返回值是 TemplateModel,也就是说,TemplateSquenceModel 实现不仅仅可以打给定的数组序列获取 对象
也得以承担在回来她之前包装该值。为了解决是题材,典型的 TemplateSquenceModel 实现以见面储存它们创建的 ObjectWrapper,之后更调用该 ObjectWrapper 来包装包含的值。相同之逻辑代表了 TemplateHashModel 或外的TemplateModel,它是另外 TemplateModel 的器皿。
因此,通常不论值的层次结构有多可怜,所有值都见面叫同一个 ObjectWrapper 包装。(要创建 TemplateModel 的落实类似,请按照这条件,可以行使 freemarker.template.WrappingTemplateModel 作为基类。)

数据模型本身(root变量)是 TemplateHashModel
在 Template.process 中指定的root对象将会让当 object_wrapper 配置中安装的对象包装器所包装,并有一个 TemplateHashModel。从此,被含有值的包裹遵循之前描述的逻辑
(比如,容器负责打包它的子例)。

表现好的目标包装器都见面绕了曾经落实 TemplateModel 接口的靶子。如果以已经落实 TemplateModel 的对象放置数据模型中
(或者由沙盘被调用的Java方法返回这个目标),那么尽管足以避实际的靶子包装。
当特别是透过沙盘访问创建的价值时,通常会这么做。因此,要避免再多地方对象包装的属性问题,
但也堪准确控制模板可以观看底情节(不是冲当前目标包装器的映射策略)。
常见的应用程序使用该手段是采取 freemarker.template.SimpleHash 作为数据模型的根root(而未是Map),当使用 SimpleHash 的 put 方法来填充(这点老重要,它不会见复制已经填充并在的 Map)。这会加快顶层数据模型变量的访问速度。

默认对象包装器

object_wrapper Configuration 的默认设置是 freemarker.template.DefaultObjectWrapper 实例。除非有特别之需求,那么建议采取这个目标包装器,或者是从定义之 DefaultObjectWrapper 的子类。

其见面识别大部分着力的Java类型,比如 String, NumberBoolean, DateList (通常还有整个的 java.util.Collection 类型),
数组,Map对等。并将其当地包裹成匹配 TemplateModel 接口的目标。它呢会见使 freemarker.ext.dom.NodeModel 来包装W3C
DOM结点,
所以可以老有益地处理XML, 在XML章节会有描述)。
对于Jython对象,会代理及 freemarker.ext.jython.JythonWrapper上。
而对于其他具有目标,则会调用 BeansWrapper.wrap(超类的不二法门),
暴露出目标的JavaBean属性作为哈希表项
(比如FTL中之myObj.foo 会在后边调用 getFoo()),
也会见暴露出目标(比如FTL中的 myObj.bar(1, 2) 就会调用方法)
的国有方法(JavaBean
action)。(关于目标包装器的再次多信息,请参阅 该章节。)

关于 DefaultObjectWrapper 更多值得注意的细节:

  • 莫用时利用其的构造方法,而是采取 DefaultObjectWrapperBuilder 来创建它。
    这即许 FreeMarker 使用单例。
  • DefaultObjectWrapper 有 incompatibleImprovements 属性, 这当
    2.3.22
    或再次胜似版本被凡是极力推荐的(参看该功能的 API文档)。如何来安:

    • 一经都以 2.3.22
      或更胜似版本的 Configuration 中设置了 incompatible_improvements 选项,
      而没有设置object_wrapper 选项(那么它们就保留默认值),
      我们不怕什么还召开不了了,因为其既以了扳平incompatibleImprovements 属性值的 DefaultObjectWrapper 单例。
    • 另外为得以当 Configuration 中独立设置 incompatibleImprovements。基于什么创建/设置 ObjectWrapper,可以透过这样好
      (假设想要 incompatibleImprovements 2.3.22):

      • 而用了构建器API:
      • 要么下构造方法:
      • 要采取 object_wrapper 属性
        (*.properties 文件或 java.util.Properties 对象):
      • 或者通过 FreemarkerServlet 配置 object_wrapper 和在 web.xml 中的 init-param 属性来布局:
  • 当初的要么测试覆盖好的类型遭到,也建议安装 forceLegacyNonListCollections 属性为 false
    如果下 .properties 或FreemarkerServlet 初始化参数,就见面像 DefaultObjectWrapper(2.3.22, forceLegacyNonListCollections=false)
    同时,使用Java
    API可以在 DefaultObjectWrapperBuilder 对象调用 build() 之前调用setForceLegacyNonListCollections(false)
  • 自定义 DefaultObjectWrapper 的极度常用方法是挂 handleUnknownType 方法。

... = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_22).build()

... = new DefaultObjectWrapper(Configuration.VERSION_2_3_22)

object_wrapper=DefaultObjectWrapper(2.3.21)

§  <init-param>

§      <param-name>object_wrapper</param-name>

§      <param-value>DefaultObjectWrapper(2.3.21)</param-value>

</init-param>

于定义对象包装示例

我们而有一个应用程序特定的切近,像下这样:

package com.example.myapp;

 

public class Tupple<E1, E2> {

    public Tupple(E1 e1, E2 e2) { ... }

    public E1 getE1() { ... }

    public E2 getE2() { ... }

}

要想为模板将其当长度为2底班,那么即使好这样来调用 someTupple[1], <#list someTupple >,
或者 someTupple?size。需要创造一个 TemplateSequenceModel 实现来适配 Tupple 到 TempateSequenceMoldel 接口:

package com.example.myapp.freemarker;

 

...

 

public class TuppleAdapter extends WrappingTemplateModel implements TemplateSequenceModel,

        AdapterTemplateModel {

    

    private final Tupple<?, ?> tupple;

    

    public TuppleAdapter(Tupple<?, ?> tupple, ObjectWrapper ow) {

        super(ow);  // coming from WrappingTemplateModel

        this.tupple = tupple;

    }

 

    @Override  // coming from TemplateSequenceModel

    public int size() throws TemplateModelException {

        return 2;

    }

    

    @Override  // coming from TemplateSequenceModel

    public TemplateModel get(int index) throws TemplateModelException {

        switch (index) {

        case 0: return wrap(tupple.getE1());

        case 1: return wrap(tupple.getE2());

        default: return null;

        }

    }

 

    @Override  // coming from AdapterTemplateModel

    public Object getAdaptedObject(Class hint) {

        return tupple;

    }

    

}

有关类和接口:

  • TemplateSequenceModel: 这就算是干什么模板将它们便是序列
  • WrappingTemplateModel
    只是一个方便使用的类,用于 TemplateModel 对象开展自身包装。通常仅针对含有其他对象的对象要。
    参考上面的 wrap() 调用。
  • AdapterTemplateModel
    表明模板模型适配一个曾在的对象到 TemplateModel 接口,
    那么去丢包装就见面于起原本对象。

末段,我们报告 FreeMarker
用 TuppleAdapter (或者,可以当拿其传递到FreeMarker之前手动包装它们)
包装 Tupple。那样的话,首先创建一个自定义的目标包装器:

package com.example.myapp.freemarker;

 

...

 

public class MyAppObjectWrapper extends DefaultObjectWrapper {

 

    public MyAppObjectWrapper(Version incompatibleImprovements) {

        super(incompatibleImprovements);

    }

    

    @Override

    protected TemplateModel handleUnknownType(final Object obj) throws TemplateModelException {

        if (obj instanceof Tupple) {

            return new TuppleAdapter((Tupple<?, ?>) obj, this);

        }

        

        return super.handleUnknownType(obj);

    }

    

}

这就是说当配置 FreeMarker
(关于配置,参考这里…)
将我们的对象包装器插在:

// Where you initialize the cfg *singleton* (happens just once in the application life-cycle):

cfg = new Configuration(Configuration.VERSION_2_3_22);

...

cfg.setObjectWrapper(new MyAppObjectWrapper(cfg.getIncompatibleImprovements()));

抑或采取 java.util.Properties 来代替部署 FreeMarker
(也就算是 .properties 文件):

object_wrapper=com.example.myapp.freemarker.MyAppObjectWrapper(2.3.22)

 

 

3、配置

 

3.1 共享变量

 

Shared
variables
 (共享变量)是为有着模板定义之变量。可以下 setSharedVariable 方法为配置中长共享变量:

Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);

...

cfg.setSharedVariable(“warp”, new WarpDirective());

cfg.setSharedVariable(“company”, “Foo Inc.”);

当装有应用此布局的沙盘被,名吧 wrap 的用户从定义指令与一个誉为吧 company 的字符串将会晤以数据模型的根root上可见,
那即便甭于根哈希表上平等赖而平等潮的丰富她。在传递给 Template.process 的
根root对象里的变量将会晤躲同名的共享变量。

Warning!

一旦安排对象在多线程环境遭受动用,不要动 TemplateModel 实现类似来作共享变量,
因为它是匪是线程安全的!
这为是冲Servlet应用程序的一枝独秀气象。

出于为后相当的风味,共享变量的集合初始化时
(就是对新的 Configuration 实例来说)不可知为空。
它含下列用户从定义指令(用户从定义指令下时得用 @ 来替#):

名称

capture_output

freemarker.template.utility.CaptureOutput

compress

freemarker.template.utility.StandardCompress

html_escape

freemarker.template.utility.HtmlEscape

normalize_newlines

freemarker.template.utility.NormalizeNewlines

xml_escape

freemarker.template.utility.XmlEscape

 

 

 

 

3.2 配置安装

Settings(配置安装)
是震慑FreeMarker行为之就给命名的值。配置安装有那么些,
例如:locale,number_format,default_encoding, template_exception_handler。可以参考 Configuration.setSetting(…)的Java
API
文档 来查看配置安装的整列表。

布安装存储于 Configuration 实例中,可以当 Template 实例中受遮盖。比如,在配备中叫 locale 设置也 “en_US”,
那么下该配置的具备模板被的 locale 都使用 “en_US”,
除非以模板被locale被显眼地安装成任何不同之价(参见 localization)。
因此,在Configuration 中的值充当默认值,
这些价值在每个模板被也得吃掩。在 Configuration 或 Template 实例中之值吗可以当独调用Template.process 方法后叫覆盖。
对于每个调用了 freemarker.core.Environment 对象的价在里面创立时虽有所模板执行的运行时环境,也包罗了大级别为遮盖了之设置信息。
在模板执行时,那里存储的价为得以给转,所以模板本身吗得安装配置信息,
比如在出口中途来变换 locale 设置。

部署信息可叫想象变为3重叠(Configuration, Template,Environment),
最高层包含特定的价,它为设置信息提供极管用的价。
比如(设置信息A到F仅仅是啊这个示例而构想的):

 

Setting A

Setting B

Setting C

Setting D

Setting E

Setting F

Layer 3: Environment

1

1

Layer 2: Template

2

2

2

Layer 1: Configuration

3

3

3

3

安排信息之有效值为:A=1,B=2,C=3,D=1,E=2。
而F的装则是 null,或者以公收获其的时节用废弃来特别。

咱俩看如何准确安装配置信息:

  • Configuration 层:
    原则达成设置配置信息时以 Configuration 对象的setter方法,例如:

  • Configuration myCfg = new
    Configuration(Configuration.VERSION_2_3_23);
  • myCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

myCfg.setDefaultEncoding(“UTF-8”);

当审用 Configuration 对象
(通常在初始化应用程序时)之前来配置它,后面要以那个就是只读的目标。

在实践中,比如很多Web应用框架中,就应该使用这种框架特定的部署方式来进展配备,
比如使用成对的 String 来配置(像在.properties 属性配置文件中那么)。
在这种气象下,框架的撰稿人大多数运 Configuration 对象的 setSetting(String
name, String value) 方法。
这可参见setSetting的API文档 部分来取可用的安名以及参数的格式的音信。
而于Spring框架中,我们得如此进行:

<bean id=”freemarkerConfig”

   
class=”org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer”>

  <property name=”freemarkerSettings”>

    <props>

      <prop key=”incompatible_improvements”>2.3.23</prop>

      <prop
key=”template_exception_handler”>rethrow</prop>

      <prop key=”default_encoding”>UTF-8</prop>

    </props>

  </property>

</bean>

央留心,这种样式的部署( String 键-值对)
和一直动用 Configuration 的API相比, 很倒霉地叫限制了。

Template 层:对于被求的本地化信息,模板的 locale 设置由 Configuration.getTemplate(…) 来装。
否则,就不可知在此地进行设置,除非想控制 Template 对象来替 freemarker.cache.TemplateCache,这样的话,
应该在 Template 对象第一涂鸦让运前纵装配置信息,
然后哪怕将 Template 对象便是是特念之。

Environment 层:这里产生半点种配备方式:

  • 采用Java
    API:使用 Environment 对象的setter方法。当然想只要以模板执行前来开,然后当调用myTemplate.process(…) 时会逢题目,
    因为以里边创立 Environment 对象后就就实施模板了,
    导致没有机会来开展设置。这个题材的解决得据此脚两只步骤进行:

  • Environment env = myTemplate.createProcessingEnvironment(root, out);
  • env.setLocale(java.util.Locale.ITALY);
  • env.setNumberFormat(“0.####”);

env.process();  // process the template

  • 当模板被(通常这给当是糟糕的做法)直接以 setting 指令,例如:

  • <#setting locale=”it_IT”>

<#setting number_format=”0.####”>

  • 当当下层,当什么时候改变配置信息,是没限制的。

一经知道 FreeMarker 支持什么的布置信息还有其的义,
可以先看FreeMarker Java API文档中之下面就有的情:

  • 每当三重叠中 freemarker.core.Configurable 的setter方法来安排。
  • 只于 Configuration 层可用之 freemarker.template.Configuration 的setter方法来配置。
  • 于三叠中可用 String 键-值对写的 freemarker.core.Configurable.setSetting(String,String) 配置。
  • 徒于 Configuration 层中可用 String 键-值对写的 freemarker.template.Configuration.setSetting(String,
    String) 配置。

 

3.3 模板加载

 

模板加载器

模板加载器是加载基于抽象模板路径下,比如 "index.ftl" 或 "products/catalog.ftl" 的原生文本数据对象。
这是因为具体的沙盘加载器对象来确定它们获取要数据经常采取了安的数来源
(文件夹着的公文,数据等等)。当调用 cfg.getTemplate (这里的 cfg 就是Configuration 实例)时,
FreeMarker询问模板加载器是否就也 cfg 建立返回给定模板路径的文本,之后
FreeMarker 解析文本生成模板。

内建模板加载器

在 Configuration 中得以采用下的方来方便建立三栽模板加载。
(每种艺术都见面以该内部新建一个模板加载器对象,然后创建Configuration 实例来采取它们。)

void setDirectoryForTemplateLoading(File dir);

void setClassForTemplateLoading(Class cl, String prefix);

void setServletContextForTemplateLoading(Object servletContext, String path);

上述的第一种植方式以磁盘的文件系统上设置了一个显眼的目录,
它规定了自乌加载模板。不要说可能,File 参数肯定是一个设有的目录。否则,将会见废弃来十分。

仲种植调用方法应用了一个 Class 类型的参数与一个前缀。这是让你来指定什么时候经过一致的机制来加载模板,
不过是因此Java的ClassLoader 来加以载类。
这就意味着传入的class参数会于 Class.getResource() 用来调用方法来找到模板。参数 prefix 是给模板的名目来加以前缀的。在实际上运行的环境被,
类加载机制是首选用来加载模板的计,通常情况下,从接近路径下加载文件之这种体制,
要较从文本系统的一定目录位置加载安全又简单。在终极的应用程序中,
所有代码都动 .jar 文件包也是天经地义的,
这样用户就是好直接执行包含有资源的 .jar 文件了。

老三栽调用方式要Web应用之上下文和一个基路径作为参数,
这个基路径是Web应用到底路径(WEB-INF目录的顶头上司目录)的相对路径。
那么加载器将见面由Web应用目录开始加载模板。尽管加载方法对莫包装的 .war 文件由作用,因为它们采用了ServletContext.getResource() 方法来访问模板,
注意这里我们负的凡“目录”。如果疏忽了第二个参数(或行使了""),
那么就可以勾兑存储静态文件(.html.jpg等)
和 .ftl 文件,只是 .ftl 文件可以给送及客户端执行。
当然不可不以 WEB-INF/web.xml 中配置一个Servlet来拍卖URI格式为 *.ftl 的用户请求,否则客户端无法得到到模板,
因此若拿会看到Web服务器被起的机要提示内容。在站点中不克用空路径,这是一个问题,
你当当 WEB-INF 目录下的有位置存储模板文件,
这样模板源文件就无见面有时地为实践及,这种体制对servlet应用程序来加载模板来说,
是非常好用的计,而且模板可以自动更新而非欲还开Web应用程序,
但是对于类似加载机制,这样虽不算了。

自打多单位置加载模板

若需要由多个职务加载模板,那就是不得不为每个位置还实例化模板加载器对象,
将她包装至一个曰 MultiTemplateLoader 的出格模板加载器,
最终以这个加载器传递让 Configuration 对象的 setTemplateLoader(TemplateLoader loader)法。
下面为来一个用类似加载器从零星个不同职务加载模板的示范:

import freemarker.cache.*; // template loaders live in this package

 

...

 

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

现行,FreeMarker将会尝试从 /tmp/templates 目录加载模板,如果在此目录下没有意识要的沙盘,它便会继续品尝从/usr/data/templates 目录下加载,如果还是没有意识要的模版,
那么它们就是见面下类似加载器来加载模板。

自任何资源加载模板

要是内建的类似加载器都未合乎利用,那么尽管用来修好之好像加载器了,
这个类似需要实现 freemarker.cache.TemplateLoader 接口,
然后将它们传递给 Configuration 对象的 setTemplateLoader(TemplateLoader loader)办法。
可以阅读API JavaDoc文档获取更多信息。

要模板需要经URL访问其他模板,那么即使不需要实现 TemplateLoader 接口了,可以选择子接口freemarker.cache.URLTemplateLoader 来替代,
只需要兑现 URL getURL(String templateName) 方法即可。

模板名称(模板路径)

分析模板的称号(也便是模板路径)是出于模板解析器来支配的。
但是一旦同其余对路线的格式要求很严苛的机件一起以。通常来说,
强烈建议模板加载器使用URL风格的门路。
在URL路径(或在UN*X路径)中符号有外含义时,那么路径中毫无动 /(路径分隔符)字符,. (同目录符号)和..(父目录符号)。字符 *(星号)是为保存的,
它用来FreeMarker的 “模板获取” 特性。

://(或者以 template_name_format 配置安装及 DEFAULT_2_4_0: (冒号)
字符)是受封存用来指定体系部分的,和URI中之形似。比如 someModule://foo/bar.ftl 使用 someModule,或者一旦 DEFAULT_2_4_0 格式,classpath:foo/bar.ftl 使用 classpath 体系。解释系统部分了是因为 TemplateLoader 决定。
(FreeMarker核心仅仅知道体系之想法,否则它们不可知正常处理相对模板名称。)

FreeMarker通常在以路径传递至 TemplateLoader 之前把其健康,所以路径中未会见含有 /../ 这样的内容,
路径会相对于虚构的模板根路径(也就算是其不会见盖 / 开头)。
其中为不见面包含 *,因为模板获取有在怪早的号。
此外,将 template_name_format 设置为DEFAULT_2_4_0,多单连续的 / 将会见于拍卖成独立的 / (除非她是 :// 模式分隔符的平部分)。

请小心,不管主机运行的操作系统是呀, FreeMarker
模板加载时常常使用斜线(而不是相反斜线)。

模板缓存

FreeMarker
是碰头缓存模板的(假设下 Configuration 对象的主意来创造 Template 对象)。这虽是说当调用 getTemplate术时,FreeMarker不但返回了 Template 对象,而且还见面用她存储于缓存中,
当下相同涂鸦再坐平等(或等)路径调用 getTemplate 方法时,
那么她仅仅回缓存的 Template 实例, 而休会见更加载与剖析模板文件了。

设转了模版文件,当下次调用模板时,FreeMarker
将会晤活动还载入和分析模板。
然而,要反省模板文件是否变动内容了凡待时日的,有一个 Configuration 级别之设置为称作”更新延迟”,它好据此来布局是时空。
这个时间哪怕打上次针对某个模板检查更新后,FreeMarker再次检查模板所假设间隔的时日。
其默认值是5秒。如果想要看模板就更新的效力,那么就使拿它们装为0。
要注意某些模板加载器也许在模板更新时或者会见生出题目。
例如,典型的例证就是以冲类加载器的模板加载器就不见面小心到模板文件内容之转。

当调用了 getTemplate 方法时,
与此同时FreeMarker意识到是模板文件已给移除了,所以这模板也会于缓存中移除。
如果Java虚拟机认为会生出内存溢出时,默认情况其会自缓存中移除任意模板。
此外,你还足以采用 Configuration 对象的 clearTemplateCache 方法手动清空缓存。

何时将一个深受缓存了的模板清除的实在用策略是由安排的属于性 cache_storage 来确定的,通过是特性可以安排外 CacheStorage 的落实。对于绝大多数用户来说,
使用 freemarker.cache.MruCacheStorage 就够了。
这个缓存存储实现了二级最近采取的复苏存。在率先级缓存中,
组件都于显眼引用到一定的顶酷数额(引用次数最多的零部件不会见为Java虚拟机抛弃,
而引用次数很少的机件则反)。当过最可怜数目时,
最近起码使用的组件将给送及二级缓存中,在那里她吃深少引用,
直到上任何一个极特别之数目。引用强度的深浅可以由构造方法来指定。
例如,设置明显部分也20,轻微部分也250:

cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))

或者,使用 MruCacheStorage 缓存, 它是默认的休息存存储实现:

cfg.setSetting(Configuration.CACHE_STORAGE_KEY, "strong:20, soft:250");

当创建了一个新的 Configuration 对象时,
它应用一个 strongSizeLimit 值为0的 MruCacheStorage 缓存来初始化,softSizeLimit 的值是 Integer.MAX_VALUE (也不怕是以骨子里被,是最最好的)。但是采取非0的 strongSizeLimit 对于高负载的服务器来说或许是一个还好之国策,对于少量引用的组件来说,
如果资源消耗都很高的话,Java虚拟机往往会吸引更胜之资源消耗,
因为其不断打缓存中丢掉来时应用的模板,这些模板还只能又加载与分析。

 

 

 

中文手册下载:

https://sourceforge.net/projects/freemarker/files/chinese-manual/

 

相关文章

网站地图xml地图