- 浏览: 275578 次
- 性别:
- 来自: 湖南岳阳
最新评论
-
ternus:
兄弟,我用boboBrowse 也遇到了排序的问题,上线了讨论 ...
lucene 分组 bobo-Browse 排序的问题 -
luli0822:
Awesome bookmarks of those guru ...
流行的jQuery信息提示插件(jQuery Tooltip Plugin) -
shenbai:
如果你要在前台运行,你应该run得是ElasticSearch ...
ElasticSearch 源码分析 环境入门 -
cl1154781231:
<s:peroperty value="#at ...
关于Struts2中标签的一些心得 -
RonQi:
转载的吗?http://blog.csdn.net/stray ...
利用bobo-browse 实现lucene的分组统计功能
FreeMarker 是一个采用 Java 开发的模版引擎,是一个基于模版生成文本的通用工具。 FreeMarker 被设计用来生成 HTML Web 页面,特别是基于 MVC 模式的应用程序。虽然 FreeMarker 具有一些编程的能力,但通常由 Java 程序准备要显示的数据,由 FreeMarker 生成页面,并通过模板显示准备的数据(如下图)。
FreeMarker 非常简单,只需要一个 Freemarker.jar 文件(无需任何配置文件)即可包含所有的功能。但 FreeMarker 的功能却是非常的强大,相比较另外一个非常著名的 Java 模版引擎 —— Velocity 来说,FreeMarker 的功能让您惊叹,但其学习的曲线也较 Velocity 要长很多。
本文主要介绍如何利用 FreeMarker 强大的可扩展性来输出各种文本信息,这不是 FreeMarker 的入门学习材料,如果您尚未对 FreeMarker 有所了解,或者还没有使用过 FreeMarker 的话,那不妨先上手后再来阅读本文。
FreeMarker 主要提供了如下几个方面的扩展性功能:
- 自定义宏
- 自定义函数
- 自定义模版文件加载器
- 缓存处理
- 异常处理
FreeMarker 和 Velocity 都提供可自定义宏的功能,但 FreeMarker 的宏功能更加强大,包括允许通过名称和参数的位置进行参数传递;允许设置参数的默认值;支持宏的嵌套;宏可以先使用再声明;支持命名空间等。
下面我们针对这些功能给出一个简单但是完整的演示例子,先看看代码:
<#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 分别指定了默认的值。
再来看看如何调用该宏:
<#include "html.ftl"> <@html title="FreeMarker 宏测试 "> 欢迎使用 FreeMarker 模版引擎 </@html> |
在 FreeMarker 中,用户自定义的宏必须以 @ 开头来调用,并传入页面标题 title 的参数。而 <@html> 标签中包含的文本“欢迎使用 FreeMarker 模版引擎”将替换宏定义中的 <#nested> 标签。因此这个模版将会生成如下的 HTML 信息:
<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 中得以解决。下面是一个简单的例子:
/** * 将标签中的代码全部转为大写并输出 * @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 ,植入该指令扩展,代码如下:
@Override protected Configuration createConfiguration() { Configuration cfg = super.createConfiguration(); cfg.setSharedVariable("upper", new UpperDirective()); return cfg; } |
在页面模版中使用<@upper>Welcome to http://www.oschina.net</@upper>
试试吧。
与宏不同,宏一般用来执行某个过程,而函数可以定义返回值,例如对一组数据求和、平均值、最大值、最小值等等运算。 FreeMarker 的函数支持可变个数的参数。例如下面定义了一个求平均值的函数:
<#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 代码来生成一个随机的整数,其代码如下:
/** * 生成一个随机的整数 * @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",new
RandomFunction());
|
使用方法:${rand(1000)} 。
模 版文件加载器用来告诉 FreeMarker 引擎到什么地方去加载模版文件。 FreeMarker 自带了三种文件加载器,分别是:文件目录加载器、类路径加载器以及 Web 上下文加载器。当在 Web 环境中使用 FreemarkerServlet 来加载模版文件时,默认使用第三种加载器,并通过 Servlet 的配置 TemplatePath 来指定模版文件所存放的路径,该路径是相对于 Web 的根目录的。
在某种情况下,我们可能会希望把模版文件的源码进行加密处 理,例如我们使用 DES 加密方式将模版源文件加密后进行存储,然后我们通过自行实现一个加密的模版文件加载器来读取这些模版文件,解密后交给 FreeMarker 引擎解释执行并得到执行的结果。 FreeMarker 为模版文件加载器定义了一个统一的接口 —— TemplateLoader ,该接口有以下四个方法:
closeTemplateSource | 关闭模版资源 |
findTemplateSource | 根据名称返回指定的模版资源 |
getLastModified | 返回模版资源最后一次修改的时间 |
getReader | 返回读取模版资源的 Reader |
为了简单起见,我们可以在 FreeMarker 自带的加载器上进行扩展,重写 getReader 方法对读取到的模版文件内容进行解密后生成一个新的 Reader 实例并返回(详细过程不再叙述)。
FreeMarker 自带的几个 TemplateLoader 分别是:
- ClassTemplateLoader :基于类路径的模版加载器
- FileTemplateLoader :基于文件目录的模版加载器
- MultiTemplateLoader :多种加载器的混合
- StringTemplateLoader :基于字符串的模版加载器
- URLTemplateLoader :基于 URL 的模版加载器
- WebappTemplateLoader :基于 Web 上下文的模版加载器
重载模版加载器后通过下面代码使之生效:
cfg.setTemplateLoader(loader) |
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 的 API 抛出来的。
模版加载异常 :模版加载异常可能是模版不存在或者没有读权限,或者是解析模版时发生错误,例如模版语法错误等。
模版执行异常 :模版执行异常是指模版已经成功的加载但在执行过程中由于代码执行错误所抛出的异常,这类异常一般都是用户的代码导致。
正常情况下,前两种异常会在开发过程中就会发现并得以解决,而第三种异常往往跟实际的运行环境和数据有关,例如由于某些数据不存在导致的空指针异常等等。因此第三种异常才是我们真正需要关心以及监控的。
为此,FreeMarker 定义了一个统一的异常处理接口 TemplateExceptionHandler 。该接口只有一个方法如下:
void handleTemplateException(TemplateException te, Environment env, java.io.Writer out) |
通过调用 cfg.setTemplateExceptionHandler 来使用自定义的异常处理方法。下面是一个简单的异常处理扩展的例子:
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()); |
为了方便编写 FreeMarker 模版,您可以使用 FreeMarker IDE 这个 Eclipse 插件。该插件具有语法高亮、错误提示等功能。虽然该插件还有很多问题,而且已经很久没更新了,但也能很好地使用。
从 上面对于 FreeMarker 的可扩展性的介绍来看,FreeMarker 确实是一个功能非常之强大的模版引擎,可以说远在 Velocity 之上。不过从使用的直观程度以及上手的时间来看,其复杂度也大大的超过了 Velocity 。当我们在面临这两个模版引擎的选择时,不能只是从功能或者容易上手的角度来决定,更应该根据业务本身的需要综合进行比较。
学习
-
查看 FreeMarker
相关信息。
-
查看 Velocity
相关信息。
-
查看 FreeMarker 的 Eclipse 编辑插件
相关信息。
- “编写自定义的 Velocity 指令
”(developerWorks,2009 年 4 月):本文通过一个实际应用例子对 Velocity 的模板语言中的指令系统进行了介绍,并演示了如何通过编写自定义的指令来扩展 Velocity 的功能。
- Java 技术专区 :寻找 Java 编程各方面的技术文章。
发表评论
-
Errors running builder 'DeploymentBuilder' on project_java
2014-10-09 11:54 750此问题一般发生在Myeclipse 保存文件并自动部署时候 ... -
MyEclipse的SVN插件的问题
2012-02-27 14:06 1721我的项目都统一使用了UTF-8编码 查看当前版本,还是提 ... -
Eclipse的SVN插件历史版本乱码的问题
2012-02-27 14:04 0我的项目都统一使用了UTF-8编码 查看当前版本,还是提 ... -
java中的io系统详解
2011-09-16 12:09 1086相关读书笔记、心得文章列表 Ja ... -
hibernate表关联注解
2011-08-02 11:39 1375好久就想玩一下hibernate注解了(因为不用hbm文件,维 ... -
TCP/IP传输层,你懂多少?
2011-05-12 17:13 1666你所不知道的传输层 题记:23页的文档上,满满当当的 ... -
关于Struts2中标签的一些心得
2011-05-06 17:21 1695最近在做Struts2+Hibernet的一个项目,在if标签 ... -
This wizard is not available because it requires a valid professional subscripti
2011-04-18 23:05 1834今天用myeclipse学习UML时,打开UML时出现了: ... -
Tomcat内存、连接数等性能参数设置
2011-04-08 09:59 1207默认参数不适合生产环 ... -
Java 垃圾回收策略调优
2011-03-28 17:52 1567JVM参数调优是一个很头 ... -
Eclipse 或 MyEclipse 不能自动编译 class文件(综合)
2011-03-25 11:50 4693有时候用Eclipse或者MyEclipse 的时候,发现不 ... -
AccessController.doPrivileged 小记
2011-03-24 11:43 1037AccessController.doPrivileged ... -
servlet 读取图片
2011-02-17 15:38 1110String path = "F:/wjbo ... -
获取文件长度http,ftp
2011-01-18 16:09 1698ftp: FTPClient ftp = new FTPCl ... -
类的设计原则
2010-12-13 18:14 1031开闭原则 Software entities ... -
收集的网站
2010-12-09 17:12 858NIO.2 入门,第 1 部分: 异步通道 API http ... -
看Hibernate源码 003 - ID Generator
2010-12-09 16:43 1314Hibernate的id生成有N种策略, 可以通过hbm文件或 ... -
看Hibernate源码 001
2010-12-09 10:40 1065我看书的方式一向是&quo ... -
Hibernate Memcached 配置
2010-12-08 15:39 2119官方网址: http://code.google.com/p/ ... -
tomcat如何配置虚拟目录及虚拟主机
2010-12-02 17:54 2153先说说如何用Eclipse来做servlet开发,那些proj ...
相关推荐
什么是FreeMarker? ................................................................................................. 7 我们应该阅读什么内容? .............................................................
Freemarker简介及标签详解大全 Freemarker简介及标签使用说明
Velocity 和 FreeMarker区别 对于大部分的应用来说,使用 FreeMarker 比 Velocity 更简单,因为 Velocity 还必须编写一些自定义的
安装freemarker插件(freemarker的eclipse插件) a) 首先下载 freemarker-ide-0.9.14.zip(在这里不用下载了,附件中已经有了) b) 解压后:把hudson.freemarker_ide_0.9.14这个文件放到:eclipse\plugins下面 c) 发现...
FreeMarker,FreeMarker
赠送jar包:freemarker-2.3.31.jar; 赠送原API文档:freemarker-2.3.31-javadoc.jar; 赠送源代码:freemarker-2.3.31-sources.jar; 赠送Maven依赖信息文件:freemarker-2.3.31.pom; 包含翻译后的API文档:...
赠送jar包:freemarker-2.3.30.jar; 赠送原API文档:freemarker-2.3.30-javadoc.jar; 赠送源代码:freemarker-2.3.30-sources.jar; 赠送Maven依赖信息文件:freemarker-2.3.30.pom; 包含翻译后的API文档:...
Freemarker页面语法 A 概念 最常用的 3 个概念 sequence 序列,对应java 里的list 、数组等非键值对的集合 hash 键值对的集合 namespace 对一个ftl 文件的引用, 利用这个名字可以访问到该ftl 文件的资源 ...
FreeMarker学习文档
freemarker\Freemarker教程_中文版
这是FreeMarker通用的分页
freemarker freemarker freemarker freemarker freemarker freemarker freemarker freemarker
FreeMarker
一篇freemarker很不错的文章 reeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组成: 1,文本:直接输出的部分 2,注释:格式部分,不会输出 3,插值:即${...}或#{...}格式的部分,将使用...
freemarker替换变量实例
camel-freemarker-1.6.4.jar, camel-freemarker-2.8.1.jar, com.springsource.freemarker-2.3.15.jar, com.springsource.freemarker-sources-2.3.15.jar, freemarker-1.4.1.jar, freemarker-2-3-18.jar, freemarker-...
Freemarker 实例,Freemarker 实例Freemarker 实例,Freemarker 实例
FreeMarker中文文档.pdf FreeMarker入门必修
FreeMarker手册-Freemarker 2.3.18。。。。。。。。。。。。。。。。。
NULL 博文链接:https://zhenghuazhi.iteye.com/blog/1923544