选择显示字体大小

用java程序生成文本的捷径

xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 


大多数程序都需要输出一些文本,比如邮件消息、html文件或控制台输出。但是,计算机本质上只能处理二进制数据,程序员必须让软件来生成可理解的文本。在这篇文章中,我要介绍的是在生成和输出文本时,为何使用模板引擎能够节省时间。你将了解模板的优点,如何针对不同的情形创建高效的模板。和system.println说再见!

虽然程序员可以很轻松地编写出输出文字信息的代码(因为这毕竟是从hello world范例学到的第一件事情),但通常而言,程序员不是写作或组织文字信息(如邮件)的最佳人选。因此,我们常常让市场部门或公关部门去做那些事情。但遗憾的是,即使对于最普通的邮件,编写者也常常依赖程序输出来完成任务。无论是对于邮件编写者还是程序员,这种合作方式都很容易带来误解和造成失误。

请看一个例子:一个java程序从某个数据源收集一些客户信息,通过email给公司的每一个客户发送帐户余额信息。下面是完成这个任务的java程序(完整的示例程序代码可以从本文最后下载):

 for (int i=0; i  {      customer customer = (customer)customers.get(i);           stringbuffer message = new stringbuffer();      message.append ("尊敬的先生/女士: ");      message.append (customer.getcustname());      message.append ("\n");      message.append ("\n");      message.append ("您的帐户余额是 ");      message.append (customer.getaccounttotal());      message.append ("\n");      message.append ("\n");      message.append ("致礼!");      message.append ("\n");      message.append ("某某装饰品公司");           // 发送email      mm.sendmail (customer.getfirstname(), customer.getemail(),         "account", message.tostring());  }


上面的例子可谓发送消息最差劲的方法之一。由于消息嵌入到了程序代码之中,如果没有程序员的帮助,其他人几乎不可能对消息进行编辑。同时,即使对于专业的程序员,如果他不了解代码,要进行编辑也很困难。如果你预见了这些麻烦,把代码写成下面这种形式:

 static public final string str_hello="尊敬的先生/女士: ";  static public final string str_message="您的帐户余额是 ";  static public final string str_bey="致礼!\n某某装饰品公司";


如果说上述代码使得消息编辑更容易,那么这种帮助也不会很多。很难要求一个不搞程序设计的人理解static和final的含义。此外,如果要改变消息的结构,上面这种代码也不够灵活。例如,人们可能要求你在邮件消息中加入更多来自数据源的信息,这时,你就得修改构造邮件的代码,或许还要添加更多的static final string对象。

模板简介
从文本文件装入消息文本可以解决部分问题——但不能提供动态内容,而这对于系统来说是很重要的。你需要有一种方法把动态内容插入到预先编写好的文本消息。但是,如果使用某种文本模板引擎,它就能够帮助你完成所有复杂的工作。

模板引擎解决了把动态内容插入文本消息的问题。使用模板引擎时,我们不再把消息直接嵌入程序,而是创建一个包含文本内容的简单文本文件,称为“文本模板”。模板引擎解析文本模板,借助一些简单的模板指令,把动态内容插入模板输出结果。

我选择的模板引擎是jakarta project的velocity,但你可以任意选择其他许多模板引擎之一。velocity和webmacro或许是当前功能最丰富、最受欢迎的两个引擎,而且两者都按照源代码开放协议免费提供。虽然我在本文例子中使用velocity,你可以方便地把这些例子移植到不同的模板引擎,只需遵照目标引擎的语法即可。
我们来看看用velocity完成的email程序例子。要编译和运行修改后的程序,你必须下载velocity并把它加入到classpath。如果要让email部分也能正常运行,你还需要javamail
 for (int i=0; i<customers.size(); i++)  {      customer customer = (customer)customers.get(i);           // 创建一个环境,并加入所有的对象      velocitycontext context = new velocitycontext();      context.put ("custname",customer.getcustname());      context.put ("total", new double (customer.getaccounttotal()));      context.put ("customer", customer );           // 解析模板,生成结果字符串      stringwriter message = new stringwriter();      template.merge(context, message);           // 发送email      mm.sendmail (customer.getfirstname(), customer.getemail(),          "account", message.tostring());  }


首先,你应该理解上面的java源代码。这里我们不再象第一个例子那样生成文本,上面的代码引用一个称为“velocity模板”的文本文件,然后把结果发送给收件人。velocity模板可以是任何文本文件,但一般它包含一些用来插入动态内容的指令。

和velocitycontext相关的部分是上述代码中最值得注意的地方。velocitycontext提供了java程序和velocity文本模板之间的连接,而velocity文本模板可以由其他人来编写。在模板中,所有加入到velocitycontext的对象都可以通过put()方法第一个参数指定的名字访问。为了解其工作过程,请看下面的模板文件:

尊敬的先生/女士: $custname您的帐户余额是 $total致礼!某某装饰品公司


velocity引擎读取模板文件时,它直接输出文件中所有的文本,但以$字符开头的除外。$符号标识着一个位置,在模板的输出结果中,对象的值应该插入到$符号所指示的位置。例如,java代码中有一个context.put ("custname",customer.getcustname())语句,当velocity模板引擎解析并输出模板的结果时,模板中所有出现$custname的地方都将插入客户的名字;即,被加入到velocitycontext的对象的tostring()方法返回值将替代velocity变量(模板中以$开头的变量)。

模板引擎中最强大、使用最频繁的功能之一是它通过内建的映像(reflection)引擎查找对象信息的能力。这个映像引擎允许用一种方便的java“.”类似的操作符,提取任意加入到velocitycontext的对象的任何公用方法的值,或对象的任意数据成员。映像引擎还带来了另外一个改进:快速引用javabean的属性。使用javabean属性的时候,我们可以忽略get方法和括号(欲知详细信息,请参考模板引擎的说明文档)。请看下面这个模板的例子。由于在前面的java代码示例中,我把customer对象加入到了velocitycontext,所以我可以把模板改写成下面这种形式:

尊敬的先生/女士: $customer.custname您的帐户余额是 $customer.accounttotal致礼!某某装饰品公司


除了替换变量之外,象velocity和webmacro这类高级引擎还能做其他许多事情。它们有用来比较和迭代的内建指令(尽管比较和迭代功能是两个模板引擎之间的共同点,但它们的语法差异很大,不能完全兼容。在选择模板引擎或者更换模板引擎时,务必注意这一点)。

举一个例子。十二月份,你的老板想要向所有的客户发一个圣诞节问候的email。你可以把这个消息加入到模板,以后再删除它。但这样的话,你得在新年那一天上班工作,以便删除圣诞问候消息。

一种更好的办法是指示模板何时显示圣诞问候消息。为此,你首先要把当前的月份加入到velocitycontext:

 int month = (new gregoriancalendar()).get(calendar.month);  // 把month值加1,因为它从0开始计算  context.put ("month", new integer(month+1) );

现在,你只需在模板中进行比较:

尊敬的先生/女士: $customer.custname您的帐户余额是 $customer.accounttotal致礼!某某装饰品公司#if ($month == 12)祝您和您的家人圣诞节快乐!#end


#if指令的作用很清楚:对一个逻辑表达式进行测试,从而决定是否在模板输出结果中包含该指令块内的内容。除了简单的等于比较之外,你还可以执行更复杂的比较,但这方面的功能都与特定的模板引擎密切关联,所以这里我不再介绍。

迭代指令和#if指令一样简单。模板引擎支持迭代java collections framework的任意实现,包括array、list和iterator。对于jdk 1.2或者更高版本,java的vector和arraylist都实现了list接口,因此它们也适合在模板引擎的迭代指令中使用。

假设我们现在不想输出帐户余额,而是想通过迭代遍历帐户的交易记录,输出详细的报表。customer对象的gettransactions()方法(参见下载包中完整的示例代码)返回一个list对象,list对象包含零个或者多个transaction对象。由于list属于java collections framework的一部分,我们可以用#foreach指令迭代其内容。我们不用担心如何定型对象的类型——映像引擎会为我们完成这个任务。从下面这个例子中,我们可以看出迭代的工作过程:

尊敬的先生/女士: $customer.custname#foreach ($transaction in $customer.transactions)$transaction.description $transaction.amount#end您的帐户余额是 $customer.accounttotal致礼!某某装饰品公司


#foreach指令的一般格式是“#foreach <item> in <list>”。#foreach指令迭代list,把list中的每个元素放入item参数,然后解析#foreach块内的内容。对于list内的每个元素,#foreach块的内容都会重复解析一次。从效果上看,它相当于告诉模板引擎说:“把list中的每一个元素依次放入item变量,每次放入一个元素,输出一次#foreach块的内容”。

mvc设计模型
在看下一个例子之前,请回顾一下前面我们所讨论的内容。使用模板引擎最大的好处在于,它分离了代码(或程序逻辑)和表现(输出)。由于这种分离,你可以修改程序逻辑而不必担心邮件消息本身;类似地,你(或公关部门的职员)可以在不重新编译程序的情况下,重新编写邮件消息。

实际上,我们分离了系统的数据模式(data model,即提供数据的类)、控制器(controller,即邮件程序)以及视图(view,即模板)。这种三层体系称为model-view-controller模型(mvc)。如果遵从mvc模型,代码分成三个截然不同的层,简化了软件开发过程中所有相关人员的工作(mvc的出现已经有一段时间,参见本文最后的“参考资源”了解更多信息)。

结合模板引擎使用的数据模式可以是任何java对象,最好是使用java collection framework的对象。控制器只要了解模板的环境(如velocitycontext),一般这种环境都很容易使用。一些关系数据库的“对象-关系”映射工具能够和模板引擎很好地协同,简化jdbc操作;对于ejb,情形也类似。

模板引擎与mvc中视图这一部分的关系更为密切。模板语言的功能很丰富、强大,足以处理所有必需的视图功能,同时它往往很简单,不熟悉编程的人也可以使用它。模板语言不仅使得设计者从过于复杂的编程环境中解脱出来,而且它保护了系统,避免了有意或无意带来危险的代码。例如,模板的编写者不可能编写出导致无限循环的代码,或侵占大量内存的代码。不要轻估这些安全机制的价值;大多数模板编写者不懂得编程,从长远来看,避免他们接触复杂的编程环境相当于节省了你自己的时间。

许多模板引擎的用户相信,在采用模板引擎的方案中,控制器部分和视图部分的明确分离,再加上模板引擎固有的安全机制,使得模板引擎足以成为其他内容发布系统(比如jsp)的替代方案。因此,java模板引擎最常见的用途是替代jsp也就不足为奇了。

html处理
由于人们总是看重模板引擎用来替换jsp的作用,有时他们会忘记模板还有更广泛的用途。到目前为止,模板引擎最常见的用途是处理html web内容。但我还用模板引擎生成过sql、email、xml甚至java源代码。在这里我只能涉及模板的部分应用,但你可以从本文最后的参考资源找到更多的例子。

我将在下面的html例子中使用前面email例子的数据模式。这个html页面是一个假想的企业intr.net页面,它显示出客户帐户的详细信息。本例中的控制器类是一个java servlet视图部分则包含一个html模板。下面的代码显示了servlet类中最主要的代码。为使这个例子更具有代表性,我从头开始手工编写这个servlet。然而,一般情况下,模板会提供一些servlet工具,帮助用户减轻一些编写代码的负担。

 // 装入模板  template template = velocity.gettemplate("html.vm");   // 创建环境  velocitycontext context = new velocitycontext();  context.put ("customers",customer.getcustomers());// 解析模板,输出应答servletoutputstream output = response.getoutputstream();writer writer = new outputstreamwriter (output);template.merge(context, writer);writer.flush();


这个例子也没有什么令人惊异的地方。和前面的例子一样,我只是把必需的对象加入到velocitycontext,然后输出解析模板的结果。但是请注意,在前面的例子中,我只把一个customer加入到velocitycontext,这里加入到velocitycontext的却是一组customer对象。我可以用#foreach指令迭代访问所有的customer对象。下面是相应的html模板:

<html><body><h1>客户报告</h1>#foreach (&#36;customer in &#36;customers)<h2>&#36;customer.custname<h2><table>#foreach (&#36;transaction in &#36;customer.transactions)<tr><td width="200">&#36;transaction.date</td><td width="150">&#36;transaction.description</td><td width="100">&#36;transaction.amount</td></tr>#end</tr><td></td><td></td><td><b>&#36;customer.accounttotal</b></td><tr></table>#end</body></html>


如果你正在规划一个工程,这个工程的需求远远超过几个html模板,请考虑众多以模板为基础的框架之一。这些框架不仅为生成html提供了模板引擎所带来的便利,而且提供许多实用工具,比如数据库连接池和安全。两个常见的例子是turbine和melati,它们都和velocity以及webmacro兼容,都是免费且源代码开放的产品。
性能和配置
对于大多数程序来说,模板的速度看来已经足够快;但对于大容量的web网站,你可能要认真地考虑一下性能问题。在性能方面,模板引擎最大的特点在于模板缓冲。在模板缓冲机制的作用下,模板不再是每次出现请求的时候从磁盘读取,而是以最理想的方式在内存中保存和解析。在开发期间,模板缓冲通常处于禁用状态,因为这时请求数量较少,而且要求对页面的修改立即产生效果。部署完毕之后,模板一般不再改变,性能就成了优先考虑的问题。因此,这时你应该启用模板缓冲功能。

对于大多数模板引擎,你可以通过应用一个设置选项或编辑java属性文件方便地启用模板缓冲功能。在velocity中,你可以通过properties对象初始化模板。至于properties对象的创建方法,你既可以手工创建,就象我前面所做的那样;或者也可以从属性文件装入。在实际应用中,后者也许是较为理想的方法。

 properties props = new properties();  props.setproperty( "file.resource.loader.cache", "true" );  props.setproperty( "file.resource.loader.modificationcheckinterval", "3600" );  velocity.init (props);


通过file.resource.loader.cache属性可以把缓冲设置成true或false,而file.resource.loader.modificationcheckinterval属性设置的是检查文件是否改变的间隔秒数。在这里我无法详细介绍所有的属性,请参考模板引擎的文档了解更多信息。

■ 结束语
免费的高级模板引擎使我们能够把模板功能加入到几乎所有的java应用。这些模板引擎为程序员提供了易用的工具,为模板编写者提供了简单的模板语言,使得开发者更有信心编写出高质量的代码。

模板分离了程序代码和应用的表现部分,极大地方便了程序员和内容制作者的工作。模板把程序员从混合了大量文本信息的杂乱代码中解放出来;使得制作文本内容的人无需面对程序逻辑,就可以轻松地编写和修改内容。

模板清楚地分离了程序逻辑和文本表现代码,从而也为设计更好的mvc系统提供了方便。因此,模板为替换其他内容发布系统(比如jsp)提供了一种有吸引力的方案,因为它能够在不增加复杂性的情况下,改进应用的整体设计。


 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons