(从csdn的blog上同步过来)
(本文发于java emag第一期)spacer.gif

关于 TemplateJSP 的起源还要追述到 Web 开发的远古年代,那个时候的人们用 CGI 来开发 web 应用,在一个 CGI 程序中写 HTML 标签。

在这之后世界开始朝不同的方向发展: sun 公司提供了类似于 CGIservlet 解决方案,但是无论是 CGI 还是 servlet 都面对同一个问题:在程序里写 html 标签,无论如何都不是一个明智的解决方案。于 sun 公司 于1999年推出了JSP技术。 而在另一个世界里,以 PHPASP 为代表的 scriptlet 页面脚本技术开始广泛应用。

不过即便如此,问题并没有结束,新的问题出现了:业务和 HTML 标签的混合,这个问题不仅导致页面结构的混乱,同时也使代码本身难以维护。

于是来自起源于 70 年代后期的 MVC 模式被引入开发。 MVC 的三个角色: Model ——包含除 UI 的数据和行为的所有数据和行为。 View 是表示 UI 中模型的显示。任何信息的变化都由 MVC 中的第三个成员来处理——控制器

在之后的应用中,出现了技术的第一次飞跃:前端的显示逻辑和后端的业务逻辑分离, COM 组件或 EJBCORBA 用于处理业务逻辑, ASPJSP 以及 PHP 被用于前端的显示。这个就是 Web 开发的 Model 1 阶段(页面控制器模式)。

不过这个开发模式有很多问题:

1.       页面中必须写入 Scriptlet 调用组件以获得所必需的数据。

2.       处理显示逻辑上 Scriptlet 代码和 HTML 代码混合交错。

3.       调试困难。 JSP 被编译成 servlet ,页面上的调试信息不足以定位错误。

这一切都是因为在 Model 1 中并没有分离视图和控制器。完全分离视图和控制器就成了必须。这就是 Model 2 。它把 Model 1 中未解决的问题——分离对组件(业务逻辑)的调用工作,把这部分工作移植到了控制器。现在似乎完美了,不过等等,原来的控制器从页面中分离后,页面所需的数据怎么获得,谁来处理页面显示逻辑?两个办法: 1. 继续利用 aspphp 或者 jsp 等机制,不过由于它们是运行在 web 环境下的,他们所要显示的数据(后端逻辑产生的结果)就需要通过控制器放入 request 流中; 2. 使用新手法——模板技术,使用独立的模板技术由于脱离的 web 环境,会给开发测试带来相当的便利。至于页面所需数据传入一个 POJO 就行而不是 request 对象。

模板技术最先开始于 PHP 的世界,出现了 PHPLIB TemplateFastTemplate这两位英雄。不久模板技术就被引入到java web开发世界里。目前比较流行的模板技术有:XSTLVelocityJDynamiTeTapestry等。另外因为JSP技术毕竟是目前标准,相当的系统还是利用JSP来完成页面显示逻辑部分,在Sun公司的JSTL外,各个第三方组织也纷纷推出了自己的Taglib,一个代表是struts tablib<o:p></o:p>

spacer.gif <o:p> </o:p>

模板技术从本质上来讲,它是一个占位符动态替换技术。一个完整的模板技术需要四个元素: 0. 模板语言, 1. 包含模板语言的模板文件, 2. 拥有动态数据的数据对象, 3. 模板引擎。以下就具体讨论这四个元素。(在讨论过程中,我只列举了几个不同特点技术,其它技术或有雷同就不重复了)

spacer.gifspacer.gif

模板语言包括:变量标识和表达式语句。根据表达式的控制力不同,可以分为强控制力模板语言和弱控制力模板语言。而根据模板语言与 HTML 的兼容性不同,又可以分为兼容性模板语言和非兼容性模板语言。

模板语言要处理三个要点:

1. 标量标记。把变量标识插入 html 的方法很多。其中一种是使用类似 html 的标签;另一种是使用特殊标识,如 Velocity 或者 JDynamiTe ;第三种是扩展 html 标签,如 tapestry 。采用何种方式有着很多考虑,一个比较常见的考虑是“所见即所得”的要求。

2. 条件控制。这是一个很棘手的问题。一个简单的例子是某物流陪送系统中,物品数低于一定值的要高亮显示。不过对于一个具体复杂显示逻辑的情况,条件控制似乎不可避免。当你把类似于 <IF condition=”$count <40”><then><span class=”highlight”>count </span></then></IF> 引入,就象我们当初在 ASPPHP 中所做得一样,我们将不得不再一次面对 scriptlet 嵌入网页所遇到的问题。我相信你和我一样并不认为这是一个好得编写方式。实际上并非所有的模板技术都使用条件控制,很多已有的应用如 PHP的以及我曾见过一个基于 ASP.NET 的应用,当然还有 JavaJDynamiTe 。这样网页上没有任何逻辑,不过这样做的代价是把高亮显示的选择控制移交给编程代码。你必需做个选择。也许你也象我一样既不想在网页中使用条件控制,也不想在代码中写 html 标记,但是这个显示逻辑是无可逃避的(如果你不想被你的老板抄鱿鱼的话),一个可行的方法是用 CSS ,在编程代码中决定采用哪个 css 样式。特别是 CSS2 技术,其 selector 机制,可以根据 html 类型甚至是 elementattributesapply 不同的样式。

3. 迭代(循环)。在网页上显示一个数据表单是一个很基本的要求,使用集合标签将不可避免,不过幸运的是,它通常很简单,而且够用。特别值得一提的是 PHP 的模板技术和 JDynamiTe 技术利用 html 的注释标签很简单的实现了它,又保持了“所见既所得”的特性。

下面是一些技术的比较:

Velocity

变量定义:用 $ 标志

表达式语句:以 # 开始

强控制语言:变量赋值: #set $this = "Velocity"

            外部引用: #include ( $1 )

            条件控制: #if …. #end

非兼容语言

JDynamiTe

变量定义:用 {} 包装

表达式语句:写在注释格式( <!--  à )中

弱控制语言

兼容语言

XSLT

变量定义: xml 标签

表达式: xsl 标签

强控制语言:外部引用: importinclude

            条件控制: if   choose…when…otherwise

非兼容语言

Tapestry

采用 component 的形式开发。

变量定义(组件定义):在 html 标签中加上 jwcid

表达式语句: ognl 规范

兼容语言

<o:p> </o:p>

spacer.gif

模板文件指包含了模板语言的文本文件。

模板文件由于其模板语言的兼容性导致不同结果。与 HTML 兼容性的模板文件只是一个资源文件,其具有良好的复用性和维护性。例如 JDynamiTe 的模板文件不但可以在不同的项目中复用,甚至可以和 PHP 程序的模板文件互用。而如 velocity 的非兼容模板文件,由于其事实上是一个脚本程序,复用性和可维护性大大降低。

spacer.gifspacer.gif

模板文件包含的是静态内容,那么其所需的动态数据就需要另外提供。根据提供数据方式的不同可以分为 3 种:

1.       Map :利用 key/value 来定位。这个是最常见的技术。如 velocityVelocityContext 就是包含了 map 对象。

Example.vm

Hello from $name in the $project project.

<o:p> </o:p>

Example.java

VelocityContext context = new VelocityContext();

context.put("name", "Velocity");

context.put("project", "<st1:city w:st="on"><st1:place w:st="on">Jakarta</st1:place></st1:city>");

<o:p> </o:p>

2.       DOM :直接操作 DOM 数据对象,如 XSLT 利用 XPath 技术。

3.       POJO :直接利用反射取得 DTO 对象,利用 JavaBean 机制取得数据。如 Tapestry

spacer.gifspacer.gif

模板引擎的工作分为三步:

1. 取得模板文件并确认其中的模板语言符合规范。

比如 velocity ,确定 #if 有对应得 #end 等。 Xmlxslt 的模型中, xml 文件标签是否完整等。在完成这些工作后,模板引擎通常会把模板文件解析成一颗节点树(包含模板文件的静态内容节点和模板引擎所定义的特殊节点)。

2. 取得数据对象。

         该数据对象一般通过程序传递引用实现。现有的大量框架在程序底层完成,处理方式也各自不同,有两种技术分别为推技术和拉技术。推技术: controller 调用 set 方法把动态数据注入,模板引擎通过 get 方法获得,典型代表: Struts ;拉技术:模板引擎根据配置信息,找到与 view 对应的 model ,调用 modelget 方法取得数据,典型代表: Tapestry

3. 合并模板文件(静态内容)和数据对象(动态内容),并生成最终页面。

         合并的机制一般如下,模板引擎遍历这颗节点树的每一个节点,并 render 该节点,遇到静态内容节点按正常输入,遇到特殊节点就从数据对象中去得对应值,并执行其表达式语句(如果有的话)。

以下详细说明:

Velocity

Template template = Velocity.getTemplate("test.wm");

Context context = new VelocityContext();

context.put("foo", "bar");

context.put("customer", new Customer());

template.merge(context, writer);

当调用 Velocity.getTemplate 方法时,将调用 ResourceManger 的对应方法。

ResourceManger 先查看该模板文件是否在 cache 中,如果没有就去获取,生成 resource 对象并调用 process() 方法,确定该模板是否有效,如果有效,则在内存中生成一个 Node 树。

当调用 template.merge() 时,遍历这颗 Node 树,并调用每个 Noderender 方法。对于模板中的变量和对象 Node ,还将调用 execute() 方法,从 context 中取得 value

   注: ResourceMangerruntime\resource 包下, Noderuntime\parser\node 包下

Tapestry

Tapestry 比较麻烦,先介绍一下 http 请求的处理过程。

httprequest 请求到达时。该请求被 ApplicationServlet 捕获,随后 ApplicationServlet 通过 getEngine 取到对应的 Engine ,通过该 enginegetService 拿到对应的 service ,调用其 service 方法执行 http 请求。

每个 service 通过 RequestCycle 对象的 getPage

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐