Tapestry
1)概述:
Tapestry 是一个全面web application 框架,是使用JAVA 写的。
Tapestry 不是一个application server,Tapestry 是一个使用在application server
中的框架。
Tapestry 不是一个application,Tapestry 是一个用来创建web application 的框
架。
Tapestry 不是JSP 的一种使用方式,Tapestry 和JSP 只能够选择一种。
Tapestry不是一个脚本环境,Tapestry使用一种组件对象模式(component object
model),这并不是一种简单的脚本,而是用于生成高动态性高互交性的web页
面。
Tapestry基于Java Servlet API version 2.2,兼容于JDK 1.2以上版本,Tapestry
通过变换多样的组件模式,将一个web application分离为一个联合组件。每一个
组件都拥有其特殊的责任用于显示web页面或者响应HTML请求。
2)Tapestry工作原理
Tapestry应用程序由几个页面组成,这些页面都是由独立的,可重复使用,
可配置的组件组成。
下面是用于描述Tapestry应用程序的基本术语:
1,页面(Page):应用程序由一堆命名唯一的页面组成,每个页面有一个模
板和若干组件;
2,模板(Template):一个用于页面(或一个组件)的HTML模板。Tapestry
中,一个模板包括基本的HTML markup,以及一些用于标记组件的特殊
属性的标签。
3,组件(Component):用于Tapestry页面的可重复使用的对象。当一个页
面表现时,或者页面中的一个链接被触发时,组件产生相应的HTML代码。
多个组件也可以用来构成一个新的组件。
4,参数(Parameter):组件拥有一些参数,用于组件属性与页面属性之间的
连接。组件通常读取自己的参数,但是一些组件(与HTML forms相关)
能够更新自己的参数,并且更新与参数绑定的页面属性。
3)Tapestry与MVC
Tapestry组件扮演着控制器Controller的角色,是模式层(Model)中
pure-domain objects和包含有组件的HTML模板之间的媒介。大多数情况下,
这种方式应用于页面(页面也是Tapestry组件),但是在某些情况中,一个组
件拥有自己的模板,包含着更多的组件,并且支持与使用者的互交。
页面通过配置一系列属性表达式(Property expressions)连接模式层和表
现层。属性表达式使用另外一种开源框架OGNL(Object Graph Navigation
Language)。OGNL的开源工程(project)独立于Tapestry,但是在Tapestry
中起很重要的作用。OGNL主要的目的在于读取和更新对象的Java Bean属性。
4)Tapestry classes
Tapestry框架由400多个类和接口组成,但是构建一个Tapestry应用程序仅
需要少数几个类,接口和方法。
1,两个关键接口:IComponent和IPage
这两个接口分别用于定义Tapestry组件和页面。所有的Tapestry代码都是继承
于接口,而不是继承于实现。所以IComponect一向被用于传递参数或者返回值,
而不是使用其实现AbstractComponent。AbstractComponent类是一个基础类,用来
实现组件。AbstractComponect是一个抽象类,定义却不实现renderComponect()
方法。它的子类实现这个方法,使用JAVA代码生成所有HTML。
BaseComponect继承AbstractComponent,增加初始化逻辑以便定位和读取一
个模板。所以大多数自定义组件继承BaseComponent。
在Tapestry中,页面作为一个特殊的组件,Ipage继承Icomponent和BasePage
继承BaseComponent。所以当要创建新页面的时候,继承BasePage。
2,三个很有用的接口:IRequestCycle,IMarkupWriter,和IEngine
大多数Tapestry页面和组件直接使用这三个接口的引用。
IRequestCycle:一个request cycle储存着当前请求的信息。它跟踪有关的活动
页面,用于响应response。在特殊事件中,它通常用来访问Servlet API 对象
(HttpServletRequest, HttpSession, HttpServletResponse)。
IMarkupWriter:一个markup复写器用来生成HTML输出,当一个页面收到一
个响应(response)的时候。它的运作很像java.io.PrintWriter,但是它包含了其它有
用的方法,以生成markup输出(包括XML-style元素和属性)。
IEngine:这是引擎是一个重要对象,用于处理Tapestry应用程序挂起。最初,
这个引擎用来维护服务器端的状态。但是它也可以作为一个处理Tapestry内部子
系统的网关。
5)关于morkup和domain object。
1,mockup:page mockups是静态HTML页面,用于表现这些动态页面在应用
程序运行时的样子。也就是指在HTML模板中,将会在应用程序运行时被
Tapestry组件替换掉的那部分旧HTML代码。Tapestry组件是动态的,当对
HTML模板做美工时,markup的存在将会提供很大的方便。这样,Tapestry
程序员可以完全与美工人员各负其责。
2,domain object:应用程序的运行,最终取决于整个团队中JAVA部分的构
架师和程序员。在大多数应用程序中,怎样连接用户接口和domain objects成
为一个问题。Domain objects是中间层对象,是应用层,在整个应用程序中,
它们是全局对象,将数据保存到数据库,或者实现你的特殊业务。通常,我
们涉及到这些问题:这些对象中储存着什么信息,怎样将不同的对象关联在
一起,以及它们怎样读取数据,或着将数据储存到数据库。
servlet作为控制器,收到请求。定位并更新domain objects,读取或更新
数据库数据。控制器servlet选择一个表现层(JSP)表现响应。表现层绘制
domain objects并最终将响应页面发送客户端。
6)页面结构:
在Tapestry应用程序中,一个页面(page)由一个HTML模块,一个页面
规范(page specification),和一个JAVA页面类(page class)构成。
每个Tapestry页面有一个特殊的唯一的名称。页面名称被用来定位页面规
范和HTML模板。页面规范的一部分用来实例化JAVA类,这部分称为页面类
(page class),包括指定应用程序中的一些特殊属性和方法。
表现(rendering)页面的第一步是实例化页面。Tapestry框架读取页面规
范和HTML模板并生成页面实例。一个Tapestry页面不是一个单一的对象。页
面对象是树对象的根对象,这些对象包括页面模板中的组件,HTML模板中
的内容,以及一些用来连接分散区域的对象。
最简单的页面:
1 一个HTML模板;
2 一个页面规范;
该规范使用XML,必须声明:
<?xml version="1.0"?>
<!DOCTYPE page-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification
3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.
dtd">
<page-specification class="hangman1.Home"/>
3 一个页面类;
该类必须继承BasePage类。
Public hangman1 extends BasePage {
}
只要HTML模板中使用Tapestry,就必须声明页面规范和页面类,即便页
面规范和页面类都没有任何属性或方法,变量。
7)关于属性标签:
a) jwcid属性:(Java Web Component ID)在模板中用来指定组件。
b) <span>标签:HTML<span>标签是一个用来包容text和elements的容器,
其本身并不能显示任何内容,仅仅是作为一个stylesheet协助对页面显示
的控制。
c) @记号:用来标明一个隐式组件。
8)监听方法(Listener method):
监听方法是普通的实例方法,其签名为:
public void method(IRequestCycle cycle)
这个方法必须是public,return void,并且只有一个类型为IRequestCycle
的参数。所有的页面和组件从AbstractComponent基础类中继承一个listeners
参数。listeners参数包含一个嵌套参数,以便类中每一个监听方法得以执行。
这里有一个接口:IActionListener,以及一些JAVA的反射机制,用来连接组
件和页面的监听方法。
一个类中可以有多个监听方法,每一个监听方法必须有一个不重复的名
字,从父类继承的监听方法同样可以通过listeners参数使用。
9)Visit对象
Visit对象是一个应用程序空间,用来储存应用程序逻辑和数据。这个对
象能被应用程序中所有的页面和组件访问,并且包含WEB应用程序中某一个
客户端的特殊信息。一个单一Visit对象实例被应用程序中所有的页面共享。
该对象类似HttpSession在典型servlet应用程序中扮演的角色。实际上,Visit
对象最终作为一个HttpSession属性被储存。
为了在应用程序中使用一些通用数据,Tapestry认可Visit对象。Tapestry
并不知道也不关心Visit对象的类型。在框架中也没有定义特殊的Visit类,每
一个应用程序自己定义Visit类。页面方法访问Visit对象时并不会指定具体的
类型:
public Object getVisit();
注意强制转换类型:
Visit visit = (Visit)getVisit();
Visit对象是框架自动生成的,在第一次运行时被引用。你必须配置
Tapestry提供实例化,一旦Visit对象生成,就将会持久化储存在
HttpSession中。
10) PageRenderListener接口
这个接口用来通知页面实例,当页面第一次运行时,应该首先执行
pageBeginRender()方法。这个方法的签名为:
public void pageBeginRender(PageEvent event)如:
11)属性指定机制(specified properties)
属性指定就是由Tapestry自动生成典型的JavaBean属性。在代码中,你定义
抽象方法用来读取和更新属性,你必须只定义需要使用的方法。Tapestry自己会
生成一个子类来实现你的方法。你甚至不用声明变量,只需要在页面规范中指明
类型即可。如:
<page-specification class="examples.Login">
<property-specification name="message"
type="java.lang.String"/>
<property-specification name="userName"
type="java.lang.String"/>
<property-specification name="password"
type="java.lang.String"/>
</page-specification>
Tapestry会自动创建一个子类来继承Login类,并实现以JavaBean方式实现变
量。这样做有三个好处:
第一:减轻程序员负担;
第二:Tapestry可以确保自动重置属性,当请求失效过期的时候。
第三:属性可以被定义为persistent。
这种机制与EJB的container-managed persistence(CMP)一样。
12) 组件的分类:
按照组件的使用方式:
a) 隐式组件:组件类型和其结构直接在HTML模板中申明的组件。通常,
Tapestry已经定义好的组件都是以隐式组件的方式使用。
b) 显示组件:其组件类型和结构储存在页面规范中。通常,自定义的组件都
是以显示组件的方式使用。
我个人倾向于将组件按照其工作方式分为三类:
1)容器组件:指组件中可以包含其它组件的组件。
目前接触到的容器组件有:Shell,Body,Conditional和Form四
种。其中最简单的是:Shell和Body,它们仅仅是用在HTML模
板的开头,用来声明<html>,<head>和<title>元素和CSS规范。
最复杂的是Form容器组件,这个组件可以包容若干组件。而这
些被包容的组件有根据它们原理上的不同,分为面向元素组件
和面向任务组件两大类。而Conditional组件根据使用情况,分
为两种,较Form简单。
2)普通组件:指容器组件以外的Tapestry定义组件,可以独立于容器组件单
独运行的组件。
3)自定义组件:
上面这种分类纯粹是我自己的理解,并不是绝对。实际上Tapestry的组件逻辑
非常复杂,再加上OGNL表达式和属性指定机制。甚至使得写注释都变得很
不容易。所以在阅读别人写的Tapestry代码的时候,难免有雾里看花,尤抱琵
琶半遮面的感觉。我的看法是,假如有看不懂的代码,暂时死记硬背先。因
为Tapestry是一个功能强大的框架,其组件的可重复使用(reusable)能力非
常强。通常例子程序的页面类中的某一个方法,就已经能够解决与此方法相
关的一系列问题。
13) 普通组件:
a) DirectLink组件:
用来生成一个从应用程序中反馈回来的特殊类型。这个组件是
Tapestry中两种主要互交产生方式之一,另外一种是user-submitted forms。
DirectLink组件表现为一个HTML<a>元素,用来提供一个URL,当用户点
击时,触发页面中一个特定的监听方法。如:
<a href="#" jwcid="@DirectLink"
listener="ognl:listeners.start">
<img src="data:images/start.png" width="250"
height="23"
border="0" alt="Start"/></a>
实际运行原理为:
组件容器通过调用RenderComponent()方法,将组件表现为Java代码。而
renderBody()方法是DirectLink组件从基础类AbstractComponent中继承的。
DirectLink组件通过renderComponent()方法来调用renderBody()方法,来表
现组件内容(被DirectLink的<a>和</a>标签包围的静态<img>标签)。
DirectLink组件有几个参数以及一个请求(listener),这个监听参数用来
找到监听方法,并且一旦用户点击链接访问WEB浏览器,监听方法就会
执行。
b)Image组件
Tapestry标准组件,用于插入<img>标签,通过image参数生成标签src
的属性。标签alt用来显示图片名称。如:
<IMG jwcid="@Image"
alt="ognl:visit.game.incorrectGuessesLeft"
image="ognl:digitImage"
height="36"
src="data:images/Chalkboard_3x8.png"
width="36" border="0"/>
这里需要介绍一下Asset:
Asset被用访问静态文件如images和stylesheets。Image组件的image参数
必须是asset object而不是String,并通过getAsset()方法作为一个object返回。
如:
public IAsset getDigitImage()
{
Visit visit = (Visit)getVisit();
int guessesLeft =
visit.getGame().getIncorrectGuessesLeft();
return getAsset("digit" + guessesLeft);
}
Asset对象执行Iasset接口,getAsset()方法从AbstractComponent基础类中继
承,能够访问在页面规范中标示为<context-asset>的元素。如:
<context-asset name="digit0"
path="images/Chalkboard_1x7.png"/>
<context-asset name="digit1"
path="images/Chalkboard_1x8.png"/>
<context-asset name="digit2"
path="images/Chalkboard_2x7.png"/>
<context-asset name="digit3"
path="images/Chalkboard_2x8.png"/>
<context-asset name="digit4"
path="images/Chalkboard_3x7.png"/>
<context-asset name="digit5"
path="images/Chalkboard_3x8.png"/>
使用Asset来定义页面规范:
c) Foreach组件
Foreach是一个循环组件,它遍历source参数,并在表现其内容前更新
value参数。这是Tapestry组件参数的至关重要特性:将一个属性与一个组件
参数绑定,组件不仅读取被绑定的属性,而且更新属性。
Foreach组件使用<span>标签,当其表现(render)时,并不直接生成
任何HTML代码。它仅仅是将内容(Text)和包含的组件重复表现。
d)Insert组件
这个组件很简单,使用起来很像JSP中的out.print()。只需要指定
value参数即可。
10)Form组件
现在讨论在Tapestry里最能激动人心,也最能让人头晕的组件:Form。
Form在HTML里与在Tapestry里有很大的联系,但是Tapestry里Form组件逻辑
和运行方式远比在HTML中复杂。对于HTML中Form的运行原理,我就不多说了,
仅仅列张表,以方便与Tapestry对比。
Tapestry引进一个全新的概念:task-oriented component面向任务组件。
Tapestry将原始的Form称为element-oriented component面向元素组件。但是
Tapestry为了衔接HTML,并没有完全抛弃面向元素组件。
下面是HTML与Tapestry相互对应的列表:
Form组件与一个监听方法绑定。Tapestry为Form中的每一个组件提供一个
ID,如同原来的name参数,用来确定各种参数值。通过OGNL来读取和更新属性,
所以程序员只需要关心OGNL。
Form组件的内部运行结构为:
name=”service”,name=”sp”和name=”Form0”是Tapestry自动添加的。
一旦form被submitted,Tapestry必须完成一些与非Tapestry应用程序一样的工
作。