`
xiangxingchina
  • 浏览: 520103 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

FreeMarker模版引擎扩展性

 
阅读更多

FreeMarker 模版引擎简介

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


图 1. FreeMarker 工作原理
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",new
RandomFunction());

 

使用方法:${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,开发者可以有效地将页面设计与后端代码分离,提高代码的维护性和可扩展性。FreeMarker的灵活性和强大的模板指令系统,使它成为许多大型项目首选的模板引擎。在学习和应用FreeMarker时,...

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

    本项目提供的就是一个无刷新导出Word文档的示例,它利用了FreeMarker模板引擎来实现这一功能。 FreeMarker是一个强大的、轻量级的模板语言,它主要用于生成动态内容,如HTML、XML、Word文档等。在这个项目中,...

    freemarker模板

    4. `TemplateDirectiveModel`和`TemplateMethodModel`: 分别表示指令和方法模型,用于实现自定义的模板扩展。 通过阅读源码,我们可以学习到FreeMarker如何解析模板,如何处理变量和表达式,以及如何执行自定义...

    基于SpringBoot实现的生成工资条源代码,使用FreeMarker模板引擎, iTextPdf生成工资条PDF

    在这个项目中,我们看到标题和描述提到了使用SpringBoot来实现工资条的生成,这涉及到两个关键的技术:FreeMarker模板引擎和iTextPdf库。 首先,FreeMarker是一个强大的模板引擎,常用于动态生成HTML或其他类型的...

    Java模板引擎之FreeMarker

    3. **可扩展性**:通过自定义指令和宏,FreeMarker可以适应各种项目需求。 4. **易用性**:其简单易懂的模板语法使非程序员也能轻松编辑模板。 **应用场景** FreeMarker常用于构建MVC架构中的View层,如Spring MVC...

    在struts2中使用freemarker模版

    在Struts2中集成FreeMarker,可以方便地将业务逻辑和视图层分离,提高代码的可维护性和可扩展性。 要开始在Struts2中使用FreeMarker模板,首先需要将`freemarker-2.3.8.jar`库文件导入到你的项目的`WEB-INF/lib`...

    使用freemarker扩展struts标签

    扩展Struts2的标签库意味着创建自定义的标签,这些标签可以嵌入到FreeMarker模板中,以提供更高级别的抽象和可重用性。这通常涉及到创建自定义的Action、模型类和标签库定义文件(TLD)。 在给定的文件列表中,我们...

    freemarker编辑插件

    Freemarker是一款强大的模板语言,常用于Web开发中的视图层渲染,特别是在Java环境中与Spring框架结合使用。它允许开发者将逻辑与展示分离,提高代码的可读性和维护性。在开发过程中,为了提高效率,我们可以使用...

    基于Freemarker模板的代码生成器前台页面模板文件

    在开发过程中,这些FTL文件会与后台Java代码配合工作,后端通过Freemarker API将数据模型传递给前端模板,模板引擎负责解析和渲染模板,生成最终的HTML页面。此外,Freemarker支持自定义函数和指令,可以扩展其功能...

    freemarker解析成pdf

    Freemarker是一个基于模板的、与语言无关的模板引擎,它的主要工作是将数据模型和模板结合,生成最终的输出。模板是由开发者设计的,包含了一些特殊的语法元素,这些元素会被Freemarker引擎替换为实际的数据值。 ...

    基于FREEMARKER模板的数据采集系统

    这种设计模式遵循了MVC(Model-View-Controller)架构,使得前端视图与后端业务逻辑分离,提高了代码的可维护性和可扩展性。 描述中提到的“博文链接:https://gaochunhu.iteye.com/blog/2352615”可能提供了一个...

    freemarker 自定义freeMarker标签

    总的来说,自定义FreeMarker标签是提高模板引擎灵活性和扩展性的有效方式,也是Java开发中的一个重要技能。通过深入理解FreeMarker的工作原理和API,开发者可以构建出更强大、更符合项目需求的模板解决方案。

    Freemarker模版开发指南

    Freemarker是一款强大的开源模板引擎,常用于Java Web应用程序中,用于生成动态HTML、XML或其他文本格式的输出。它的核心设计理念是将展示逻辑与业务逻辑分离,让开发者专注于数据处理,而将页面布局和样式交给设计...

    基于Java的模板引擎 FreeMarker.zip

    FreeMarker与Spring、Struts等框架集成良好,允许开发者将业务逻辑与视图展示分离,提高代码的可维护性和可扩展性。 FreeMarker的核心概念是模板和数据模型。模板是HTML或其他格式的静态文件,其中包含一些特定的...

    Freemarker模版实现接口和继承(下)

    Freemarker模版技术是Java领域中广泛应用的模板引擎,它允许开发者用纯文本格式来编写动态内容,然后在运行时解析这些模板并生成HTML、XML或其他格式的文本。本篇我们将深入探讨 Freemarker 中的接口实现与继承,...

    eclipse的freemarker插件

    而Freemarker则是一种轻量级的、基于模板的Java模板引擎,常用于Web应用中的动态内容生成,比如JSP替代技术。它允许开发者将业务逻辑与页面展示分离,提高代码的可维护性和可读性。 "eclipse的freemarker插件"是指...

    当前流行的模板引擎效率分析(velocity,freeMarker,Smarty4j,httl)

    5. **扩展性和灵活性**:模板引擎是否支持自定义标签、函数,以及与其他库的集成程度。 通过对《业务逻辑编译改造-模板技术分析.doc》和“httl-performance-test”文件的深入研究,我们可以获取更多关于这些模板...

    Java模版引擎Freemarker

    总的来说,FreeMarker是一个高效、灵活的模板引擎,能够帮助开发者将数据和展示分离,提高代码的可读性和可维护性。通过合理使用FreeMarker,可以简化Web应用开发中的视图层实现,让开发者更专注于业务逻辑和数据...

    FreeMarker 2.3.18

    `全面探索 FreeMarker 模版引擎的扩展性.mht`探讨了FreeMarker的自定义指令和函数,以及如何通过模板配置文件进行定制,以满足特定项目需求。 5. **Java & Game**: `有关freemarker的语法,循环,逻辑 Java & ...

    基于java的模板引擎 FreeMarker.zip

    这个过程是完全与业务逻辑分离的,提高了代码的可维护性和可扩展性。 **核心概念:** 1. **模板(Template)**:模板是FreeMarker的中心,通常是一个HTML文件,其中包含动态和静态部分。动态部分由FreeMarker的指令...

Global site tag (gtag.js) - Google Analytics