`
feiliboos
  • 浏览: 664288 次
文章分类
社区版块
存档分类
最新评论

面探索 FreeMarker 模版引擎的扩展性

 
阅读更多

FreeMarker 模版引擎简介

FreeMarker 是一个采用 Java 开发的模版引擎,是一个基于模版生成文本的通用工具。 FreeMarker 被设计用来生成 HTML Web 页面,特别是基于 MVC 模式的应用程序。虽然 FreeMarker 具有一些编程的能力,但通常由 Java 程序准备要显示的数据,由 FreeMarker 生成页面,并通过模板显示准备的数据(如下图)。


图 1. FreeMarker 工作原理

FreeMarker 非常简单,只需要一个 Freemarker.jar 文件(无需任何配置文件)即可包含所有的功能。但 FreeMarker 的功能却是非常的强大,相比较另外一个非常著名的 Java 模版引擎 —— Velocity 来说,FreeMarker 的功能让您惊叹,但其学习的曲线也较 Velocity 要长很多。

本文主要介绍如何利用 FreeMarker 强大的可扩展性来输出各种文本信息,这不是 FreeMarker 的入门学习材料,如果您尚未对 FreeMarker 有所了解,或者还没有使用过 FreeMarker 的话,那不妨先上手后再来阅读本文。

FreeMarker 主要提供了如下几个方面的扩展性功能:

  1. 自定义宏
  2. 自定义函数
  3. 自定义模版文件加载器
  4. 缓存处理
  5. 异常处理

FreeMarker 自定义宏

FreeMarker 和 Velocity 都提供可自定义宏的功能,但 FreeMarker 的宏功能更加强大,包括允许通过名称和参数的位置进行参数传递;允许设置参数的默认值;支持宏的嵌套;宏可以先使用再声明;支持命名空间等。

下面我们针对这些功能给出一个简单但是完整的演示例子,先看看代码:


清单 1. 宏定义文件 ( html.ftl )

				
<#macro html title charset="utf-8" lang="zh-CN"> 
 <html> 
 <head> 
  <meta http-equiv="Content-Type" content="text/html; charset=${charset}" /> 
  <meta http-equiv="Content-Language" content="${lang}"/> 
  <title>${title}</title> 
 </head> 
 <body> 
    <#nested> 
 </body> 
 </html> 
 </#macro>

在这个宏定义文件中,我们声明了一个名为 html 的宏,该宏是为了生成一个 HTML 页面的框架。它具有三个参数分别是 title 、charset 和 lang ,其中 charset 和 lang 分别指定了默认的值。

再来看看如何调用该宏:


清单 2. 调用宏

				
<#include "html.ftl"> 
 <@html title="FreeMarker 宏测试 "> 
	欢迎使用 FreeMarker 模版引擎
 </@html>

在 FreeMarker 中,用户自定义的宏必须以 @ 开头来调用,并传入页面标题 title 的参数。而 <@html> 标签中包含的文本“欢迎使用 FreeMarker 模版引擎”将替换宏定义中的 <#nested> 标签。因此这个模版将会生成如下的 HTML 信息:


清单 3. 模版生成结果

				
<html> 
 <head> 
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  <meta http-equiv="Content-Language" content="zh-CN"/> 
  <title>FreeMarker 宏测试 </title> 
 </head> 
 <body> 
	欢迎使用 FreeMarker 模版引擎
 </body> 
 </html>

而 Velocity 本身并不提供嵌套模版的功能,它必须依赖 Velocity-Tools 这个项目来实现。另外对于一些需要实现更复杂逻辑的宏,还可以通过 Java 类来进行定义。 FreeMarker 提供了一个 TemplateDirectiveModel 接口,通过实现该接口可以实现自定义宏的功能,这样可以更好的跟应用逻辑进行集成,不过需要注意的是暂不支持通过参数的位置来调用宏,调用时必须指定参数名,该问题将在 FreeMarker 2.4 中得以解决。下面是一个简单的例子:


清单 4. 自定义宏功能的例子

				
/**
 * 将标签中的代码全部转为大写并输出
 * @author Winter Lau (javayou@gmail.com)
 * 使用方法:
 * <@upper>Welcome to http://www.oschina.net</@upper>
 */
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();
        }
    }

}

接下来我们需要重载 FreemarkerServlet ,植入该指令扩展,代码如下:


清单 5. 重载 FreemarkerServlet

				
@Override
protected Configuration createConfiguration() {
	Configuration cfg = super.createConfiguration();
	cfg.setSharedVariable("upper", new UpperDirective());
	return cfg;
}

在页面模版中使用<@upper>Welcome to http://www.oschina.net</@upper>试试吧。

FreeMarker 自定义函数

与宏不同,宏一般用来执行某个过程,而函数可以定义返回值,例如对一组数据求和、平均值、最大值、最小值等等运算。 FreeMarker 的函数支持可变个数的参数。例如下面定义了一个求平均值的函数:


清单 6. 求平均值的函数例子

				
<#function avg nums...> 
  <#local sum = 0> 
  <#list nums as num> 
    <#local sum = sum + num> 
  </#list> 
  <#if nums?size != 0> 
    <#return sum / nums?size> 
  </#if> 
 </#function>

其中函数名为 avg ,支持可变个数的参数 nums 。可用下面的代码来要调用该函数:

${avg(3,5,100,3453)}

跟宏相同,FreeMarker 也可以用 Java 来编写自定义函数。例如我们用 Java 代码来生成一个随机的整数,其代码如下:


清单 7. 使用 Java 编写的自定义函数

				
/**
 * 生成一个随机的整数
 * @author Winter Lau (javayou@gmail.com)
 * @url http://www.oschina.net
 */
public class RandomFunction implements TemplateMethodModel {

	final static Random rnd_seed = new Random(System.currentTimeMillis());
	
	/* (non-Javadoc)
	 * @see freemarker.template.TemplateMethodModel#exec(java.util.List)
	 */
	@SuppressWarnings("unchecked")
	public Object exec(List args) throws TemplateModelException {
		return rnd_seed.nextInt(Integer.parseInt((String)args.get(0)));
	}

}

同样的,需要将该函数的定义植入 FreeMarker :

cfg.setSharedVariable("rand",newRandomFunction());

使用方法:${rand(1000)} 。

FreeMarker 自定义模版文件加载器

模版文件加载器用来告诉 FreeMarker 引擎到什么地方去加载模版文件。 FreeMarker 自带了三种文件加载器,分别是:文件目录加载器、类路径加载器以及 Web 上下文加载器。当在 Web 环境中使用 FreemarkerServlet 来加载模版文件时,默认使用第三种加载器,并通过 Servlet 的配置 TemplatePath 来指定模版文件所存放的路径,该路径是相对于 Web 的根目录的。

在某种情况下,我们可能会希望把模版文件的源码进行加密处理,例如我们使用 DES 加密方式将模版源文件加密后进行存储,然后我们通过自行实现一个加密的模版文件加载器来读取这些模版文件,解密后交给 FreeMarker 引擎解释执行并得到执行的结果。 FreeMarker 为模版文件加载器定义了一个统一的接口 —— TemplateLoader ,该接口有以下四个方法:

closeTemplateSource 关闭模版资源
findTemplateSource 根据名称返回指定的模版资源
getLastModified 返回模版资源最后一次修改的时间
getReader 返回读取模版资源的 Reader

为了简单起见,我们可以在 FreeMarker 自带的加载器上进行扩展,重写 getReader 方法对读取到的模版文件内容进行解密后生成一个新的 Reader 实例并返回(详细过程不再叙述)。

FreeMarker 自带的几个 TemplateLoader 分别是:

  1. ClassTemplateLoader :基于类路径的模版加载器
  2. FileTemplateLoader :基于文件目录的模版加载器
  3. MultiTemplateLoader :多种加载器的混合
  4. StringTemplateLoader :基于字符串的模版加载器
  5. URLTemplateLoader :基于 URL 的模版加载器
  6. WebappTemplateLoader :基于 Web 上下文的模版加载器

重载模版加载器后通过下面代码使之生效:

cfg.setTemplateLoader(loader)

FreeMarker 缓存处理

FreeMarker 的缓存处理主要用于模版文件的缓存,一般来讲,模版文件改动不会很频繁,在一个流量非常大的网站中,如果频繁的读取模版文件对系统的负担还是很重的,因此 FreeMarker 通过将模版文件的内容进行缓存,来降低模版文件读取的频次,降低系统的负载。

当处理某个模版时,FreeMarker 直接从缓存中返回对应的 Template 对象,并有一个默认的机制来保证该模版对象是跟模版文件同步的。如果使用的时候 FreemarkerServlet 时,有一个配置项 template_update_delay 用来指定更新模版文件的间隔时间,相当于多长时间检测一下是否有必要重新加载模版文件,0 表示每次都重新加载,否则为多少毫秒钟检测一下模版是否更改。

FreeMarker 定义了一个统一的缓存处理接口 CacheStorage ,默认的实现是 MruCacheStorage 最近最少使用的缓存策略。一般情况下,很少需要对缓存进行扩展处理。您可以通过下面的代码指定最大缓存的模版数:

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

其中第一个参数是最大的强引用对象数,第二个为最大的弱引用对象数。这两个值 FreeMarker 默认的是 0 和 Integer.MAX_VALUE,表明模版缓存数是无限的。

FreeMarker 异常处理

当使用 FreeMarker 做为模版引擎的时候,可能发生的异常包括:

配置异常:配置异常指的是 FreeMarker 初始化时发生的异常,例如错误的配置导致,该异常时由 FreeMarker 的 API 抛出来的。

模版加载异常:模版加载异常可能是模版不存在或者没有读权限,或者是解析模版时发生错误,例如模版语法错误等。

模版执行异常:模版执行异常是指模版已经成功的加载但在执行过程中由于代码执行错误所抛出的异常,这类异常一般都是用户的代码导致。

正常情况下,前两种异常会在开发过程中就会发现并得以解决,而第三种异常往往跟实际的运行环境和数据有关,例如由于某些数据不存在导致的空指针异常等等。因此第三种异常才是我们真正需要关心以及监控的。

为此,FreeMarker 定义了一个统一的异常处理接口 TemplateExceptionHandler 。该接口只有一个方法如下:

void handleTemplateException(TemplateException te,
    Environment env,
    java.io.Writer out)

通过调用 cfg.setTemplateExceptionHandler 来使用自定义的异常处理方法。下面是一个简单的异常处理扩展的例子:


清单 8. 异常处理扩展的例子

				
class MyTemplateExceptionHandler implements TemplateExceptionHandler {
    
    public void handleTemplateException(TemplateException te, 
        Environment env, java.io.Writer out) 
		throws TemplateException {
        try {
            out.write("[ERROR: " + te.getMessage() + "]");
        } catch (IOException e) {
            throw new TemplateException(
                "Failed to print error message. Cause: " + e, env);
        }
    }
    
}

...

cfg.setTemplateExceptionHandler(new MyTemplateExceptionHandler());

Eclipse 的 FreeMarker 插件

为了方便编写 FreeMarker 模版,您可以使用 FreeMarker IDE 这个 Eclipse 插件。该插件具有语法高亮、错误提示等功能。虽然该插件还有很多问题,而且已经很久没更新了,但也能很好地使用。

总结

从上面对于 FreeMarker 的可扩展性的介绍来看,FreeMarker 确实是一个功能非常之强大的模版引擎,可以说远在 Velocity 之上。不过从使用的直观程度以及上手的时间来看,其复杂度也大大的超过了 Velocity 。当我们在面临这两个模版引擎的选择时,不能只是从功能或者容易上手的角度来决定,更应该根据业务本身的需要综合进行比较。

分享到:
评论

相关推荐

    freemarker模板引擎

    freemarker模板引擎的资料,适合新手使用和了解,有需要的可以下载看一看。

    Java项目中利用Freemarker模板引擎导出--生成Word文档

    在这里将通过Freemarker这个模板引擎来实现导出 Word,项目不限于Swing,SSH,SSM,Spring Boot 之类的 ————————————— Spring Boot,Freemarker 编辑好格式的Word文档 1份 将Word文档里需要填充的地方...

    FreeMarker模板引擎资料大全

    FreeMarker 是一款模板引擎:即一种基于模板、用来生成输出文本(任何来自于 HTML格式,pdf格式的文本用来自动生成源代码)的通用工具。它是为 Java 程序员提供的一个开发包,本教程绝对详细,叫你从精通,玩转它

    本oa办公系统基于springboot框架,选用了mysql数据库,前端采用freemarker模板引擎.zip

    本oa办公系统基于springboot框架,选用了mysql数据库,前端采用freemarker模板引擎.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、...

    Freemarker模板生成pdf+java

    Freemarker模板生成pdf+javaFreemarker模板生成pdf+javaFreemarker模板生成pdf+javaFreemarker模板生成pdf+javaFreemarker模板生成pdf+java

    在struts2的freemarker模板中扩展struts标签

    众所周知,struts2宣称freemarker模板中不再支持自定义标签,但如果工程UI仅用freemarker模板可以通过扩展struts标签简单实现,不是采用官方不推荐的配置JspSupportServlet实现的!内付详细说明及范例,此方法仅为团队...

    SpringBoot使用FreeMarker模板发送邮件

    通过springboot 整合freemarker模板引擎,自动发送html格式邮件

    java web项目无刷新导出word文档demo,使用freemarker模板引擎

    java web项目无刷新导出word文档demo,使用freemarker模板引擎,可以生成美观的word文档,逻辑简单易用。

    SpringBoot+FreeMarker模板引擎demo

    FreeMarker 是一款模板引擎:一种基于模板的、用来生成输出文本(任何来自于 HTML 格式的文本用来自动生成源代码)的通用工具。它是为 Java 程序员提供的一个开发包或者说是类库。它不是面向最终用户,而是为程序员...

    freemarker模板技术生成复杂word文档---解决了编码问题

    大致的思路是先用office2003或者2007编辑好word的样式,然后另存为xml,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板并输出‘.doc’ word文档。经测试这样方式生成的word文档完全符合office标准,...

    FreeMarker模板设计指南

    FreeMarker概述 FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写 FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式的应用程序 虽然FreeMarker具有一些编程的能力...

    FreeMarker+SpringBoot模板引擎demo

    FreeMarker 是一款模板引擎:一种基于模板的、用来生成输出文本(任何来自于 HTML 格式的文本用来自动生成源代码)的通用工具。它是为 Java 程序员提供的一个开发包或者说是类库。它不是面向最终用户,而是为程序员...

    根据模板,freemarker、xDoc、POI三种方式生成Word文档(含jar包)

    通过三种不同的方式(freemarker+jfreechart、xDoc、POI),根据模板创建Word文件。可以动态修改Word中表格(table)、图表(chart)等数据。导入工程后,可直接运行xxxDemo文件,查看文档生成效果。详见压缩包中的...

    基于Freemarker模板的代码生成器工具

    基于Freemarker模板的纯Java代码实现的代码生成器工具

    freemarker-ext:Freemarker模板引擎的扩展

    &lt; dependency&gt; &lt; groupId&gt;org.eluder.freemarker&lt;/ groupId&gt; &lt; artifactId&gt;freemarker-ext&lt;/ artifactId&gt; &lt; version&gt;0.9.2&lt;/ version&gt;&lt;/ dependency&gt;HTML转义Freemarker是通用模板引擎,这意味着它的范围不仅限于...

    基于SSM框架和freemarker模板引擎开发的一套p2p借款平台源代码+数据库+毕业论文

    这是一套采用SSM框架和freemarker模板引擎开发的一套p2p借款平台, 外部引用了阿里云的手机实名认证接口,美联软通的短信发送接口和腾讯邮箱的发件接口。 系统简介 系统包含两类用户,普通用户和管理员。下面展示...

    Java模板引擎 FreeMarker

    Java模板引擎 FreeMarker

    FreeMarker模板引擎 v2.3.30

    为您提供FreeMarker模板引擎下载,FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写。FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式的应用程序。虽然FreeMarker具有一些...

    freeMarker模板文件附件.zip

    freeMarker模板文件 word文件示例、ftl文件示例、输出文件示例,方便学习者自己制作模板。

    FreeMarker模板包括dto、dao、rowmapper、bo、service

    FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所...

Global site tag (gtag.js) - Google Analytics