WEB开拓模式Tapestry简介
副标题#e#
媒介
由于internet技能的遍及风行,越来越多的措施开拓模式正在从C/S向B/S转换,由于B/S模式具有瘦客户端的利益——无须安装客户端措施,这就大大减轻了软件进级的用度,所有的业务逻辑和数据库存储都放在处事端,从而提高了系统的可维护性。并且由于HTTP协议是一个开放式的尺度,因此只要是支持HTTP协议的欣赏器都可以运行,再团结今朝的XML技能,可以利便地实现跨平台的漫衍式应用。
但B/S架构有其天生的缺点:无状态性。这其实并不是B/S措施的错,而是由于B/S措施是成立在http协议的基本上的,因此措施无法维护各个客户端的状态,不外所幸的是这个问题此刻已包办理了。
现行的B/S开拓的另一大缺陷是:代码和HTML页面揉和在一起了,对措施员和美工很是倒霉,并且对代码的维护的确是一场噩梦,这相信是很多Web开拓者的一种体会。而C/S布局就没有这种缺陷,因为它是基于组件的,好比VB,你有多得数不清的ActiveX控件来完成一些巨大的界面。
也许你看到这里,心里在打定着:照旧用C/S算了,免得这么多贫苦,可是一个不敢于实验新技能的公司往往是一个被新技能裁减的公司。
为了办理上述问题,apache组织开拓了一个基于JSP的MVC模式的实现:struts。它将WEB表示层分为model-view-controller几个部门,通过一个servlet来对web的流程举办控件,这与纯真的JSP或ASP对比确实进步了不少,因为操作struts能清楚地界定web的流程,并且不倡导利用<% …. %>语句,转而代之的是taglib技能。但它始终没有挣脱代码和HTML页面揉和的问题,并且它的道理对付JSP初学者来说较量巨大,taglib库不足富厚,并且taglib也不像组件那样可以或许担任。
于是,新一代的WEB开拓要领应运而生,它们最典范的就是开拓要领是基于组件的,个中最具代表性的就是Tapestry。
为什么要用Tapestry?
Tapestry此刻已经属于Apache的Jakarta项目下了,你可以会见http://jakarta.apache.org/tapestry 来会见更多的信息。
Tapestry是一个强有力、开放源码、基于Java的用于开拓高端WEB应用的framework。它是JSP的一种替代要领,利用Tapestry,可以让你的页面看不到一句JSP代码,这使页面看起来很是清洁。它可以用很少的代码来结构一个极度巨大的WEB应用。
Tapestry利用了雷同于传统C/S的开拓要领:基于组件的开拓。利用tapestry,你就可以获得以下长处:
1、很是高的代码复用性,因为在tapestry中,任何事物都可以看作一个可复用的组件。
2、将JSP开拓者从繁琐的JSP代码中摆脱出来,取而代之的是真正面像对像要领,而不是URL理会。
3、对页面国际化的充实支持
4、准确地错误陈诉,可以将错误定位到源措施中的行,代替了JSP中那些莫名奇妙地错误提示。
充实支持团队开拓,美工人员和JAVA开拓人员可以融洽地相处,相互都不依赖于对方。
你也许会问:假如tapestry是基于组件的,那么它的组件是奈何组成的呢?它是由一个界说文件(以XML的名目)、一个HTML模板、一个JAVA类。Tapestry的组件可以组合在一起形成一个更大的组件或逻辑页面。
Tapestry的事情道理如下:在web.xml文件中界说一个名为ApplicationServlet的servlet来举办处理惩罚所有的http请求,这和struts有点相似,不外它主要职责是认真引导整个tapestry焦点,启动日志成果,读取设置文件,建设事情引擎来指派客户请求。ApplicationServlet主要是通过它的设置文件来举办自身打点的,你可以通过在web.xml中界说一个名为org.apache.tapestry.specification-path的初始参数来指定这个设置文件,虽然假如你以为这样贫苦,那你也可以不指定设置文件,但tapestry会自动寻找与此servlet同名的设置文件,譬喻在web.xml中的界说如下:
<servlet>
<servlet-name>Registration</servlet-name>
<servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
#p#副标题#e#
那么Tapestry会自动寻找一个名为Registration.application的设置文件,假如用户没有界说此文件,那么Tapestry将不能正常启动。
为了便于领略,我举一个各人都熟知的例子:用户注册模块。首先生成一个注册页面,期待用户的输入,然后举办正当性查抄,假如通过,就显示下一个页面。如下所示
也许你会说,咦,这不是和JSP的表单差不多的吗?是的,固然看上去差不多,但道理大纷歧样,由于它是基于组件的,所以Name、DOB、Address、City、State、Zip和Register按钮都是单独的组件,而不是普通的表单了。
#p#分页标题#e#
这时你必定会说:唉,也不外如此嘛,就算是基于组件的思想设计出来的对象也不外尔尔,这几个文本框这么简朴,何须费这么大的劲把它们包装成组件呢?呵呵,不知你留意到DOB这个文本框没有?它和其它几个有些纷歧样,它的右边多了一个小按钮,假如点击此按钮,呈现的功效会让你瞠目结舌!
酷吧?假如你曾经是一个JSP措施员,你的脑海里顿时会闪过这样一个动机:必然是用javascript做的! 假如你这样想的话,那你就大错特错了,因为本人的javascript还没到达这种程度,假如我汇报你完成这样一个组件,只需要在设置文件中插手通俗易懂的几个xml元素的时候,你是否会以为这真的很神奇呢?
是的,这就是Tapestry的魅力地址,Tapestry主要设计者Howard Lewis Ship的目标就是最大限度地解放JSP措施员的劳动量,让他们有更多的时间花在客户的需求和措施的逻辑设计上,而不是花在和美工人员相互办理抵牾斗嘴上,你大概会急不行待的问:那我们是否可以动手试一试了呢?是的,虽然可以,但在动手之前,我要先先容一些关于tapestry的观念,并试着开拓一个最简朴的“hello,world”措施来让你熟悉一下它的设置。因为它是一个framework,假如你对它的布局不是很清楚的话,我想你必定无法充实地操作它的。
一个Tapestry组件一般称作JWC(Java Web Component),它能与其它JWC一起组合,生成实例,举办设置。好比你适才看到的谁人不行思议的组件就是一个DatePicker组件,适才的谁人页面是一个Page组件,Tapestry自带了许多的JWC组件,这些组件不只仅只是GUI组件,尚有大概是带节制成果的组件,好比foreach组件可以或许提供轮回的成果,同时tapestry还提供了一个简朴的要领来制浸染户自界说的组件,所有的Tapestry应用的暗示层都是由这些JWC组成的。
一个简朴的Hello,world措施
为了清楚地弄清tapestry的framwork构架,我们从最简朴的”hello,world”开如,打开组件的暗箱来看一看JWC的内部布局。一个典范的JWC包罗3个部门—-一个HTML模板,一个XML名目标界说文件和一个或多个JAVA类,尽量一个简朴的JWC可以仅仅是一个HTML模板,但在本例中我们照旧利用这三个部门来展示它的内部布局。
先让我们看一下演示的功效:
由于我们知道Page也是一种JWC组件,因此让我们看一下这个Page组件是奈何组成的,首先,我们看一下它的HTML模板布局:
Home.html
<html>
<head>
<title>Welcome to Tapestry!</title>
</head>
<body>
Hello <span jwcid="user">User Name</span>! Welcome to Tapestry!
</body>
</html>
这里你大概看上去以为很是眼熟,这不就是一个普通的HTML网页吗?且慢,假如你仔细调查,会发明span标签多了一个新的属性jwcid,这正是tapestry设计的精妙之处,它的表示层可以说险些是99%的纯HTML,只是增加了一个jwcid属性,jwcid=”user”在这里的意思是在<span> … </span>之间安排一个名为user的组件。这里要指出的是jwcid属性不只仅可以放在span标签内,它可以放在任何的HTML标签内,好比<title>、<body>、<form>等等,至于为什么要选<span>的原因是<span>标签不影响网页的输出结果,这就大大利便了美工人员,他们可以直接利用frontpage或dreamwaver来举办设计,而不像JSP那样无法在frontpage或dreamwaver中正常显示。
也许你会问,网页上输出的用户名显着是Jack嘛,为什么HTML模板中是User name呢?这是因为当tapestry一旦发明某个HTML标签有jwcid的属性后,它就会把这个标签当做一个组件来对待,至于标签内里的对象到底是原样输出或忽略就要依照谁人组件的范例而定了,在这里我们的user组件实际上是一种Insert范例的组件,因此会把<span>标签内的内容忽略掉,其实也可以直接写成<span jwcid=”user”></span>,结果也是一样的,只不外加了User name后就利便了美工人员的排版设计。
请留意,HTML模板有它本身的定名类型,它的文件名应该与Page组件的界说文件沟通,只不外是扩展名为.html,与普通的web处事器一般都有一个index.html雷同,tapestry也有本身的”index.html”,只不外它的名字不是”index.html”,而是Home.html,那么这些模板文件毕竟应该放在哪儿呢?很简朴,放在当前应用的目次下面就行了,好比我们的应用是Welcome,那把它放在webapps/Welcome目次下面就行了。
Home.page
#p#分页标题#e#
<?xml version="1.0" encoding="UTF-8"?>
<!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="demo.Home">
<component id="user" type="Insert">
<binding name="value" expression="userName"/>
</component>
<context-asset name="$template" path="Home.html"/>
</page-specification>
以上这个文件即是这个名为”Home”的Page组件的页面界说文件,它仿佛是一座桥,接洽着HTML模板和JAVA类举办协同事情。这个界说文件“Home.page”实际上就是一个xml文件,它的根元素是page-specification,它有一个名为class的属性,用来指示HTML模板与哪个java类协同事情,在本例中为demo.Home这个类。
Component元素是对HTML模板中引用的组件的界说,id属性名必然要与HTML模板中的jwcid一样,type指定了这个组件属于哪种范例,在本例中为Insert组件,Insert组件是Tapestry中的一个根基组件,它的浸染是在HTML模板中插入指定的文本,待插入的文本的值由Insert的value属性来抉择,在本例中,value属性的表达式值是userName。你这时候大概会以为越看越糊涂:那为什么最后运行功效会是Jack呢?不要着急,关于这个我们顿时就要具体地举办讲授。我们照旧先看一下最后一个元素context-asset,这个元素是界说这个Page组件要用到的一些附加资源,好比图像、css名目文件等等之类的对象
此刻我们开始当真地讲一下<binding name="value" expression="userName"/>这个元素的寄义,由于HTML模板嵌入的是一个Insert范例的组件,而Insert组件的成果是插入一段文本到HTML模板中去,对付本例而言,或者可以直接地指定要插入文本的内容为“Jack”,但对一个实际的应用来说,往往是要从数据库从取出用户的名称,然后将该名称插入到HTML模板中去,那么就不能直接指定文本的内容了,怎么办呢?我们顿时想到的步伐是:指定一个JAVA类,通过javaBean的属性要领来得到数据库中的用户名,就像jsp中的<jsp getProperty … />标签一样,不错,这简直是一个好要领,tapestry正是这样做的,因此<binding name="value" expression="userName"/>这个元素中的expression的值正是demo.Home这个JAVA类的一个属性,也就是说将demo.Home这个类的userName属性绑定到Insert组件的value属性中去,而我们的首创人Howard Lewis Ship先生并没有重头对这个绑定的成果举办设计,因为在软件开拓中有一句著名的话:”不要反复地发现同一个轮子“,这个绑定成果是由另一个开放源码的东西完成地—- Object Graph Navigation Library,简称OGNL(关于OGNL详细可以参考http://www.ognl.org )。其内部真正的转换如下图所示:
个中,Insert这个JWC组件有一个名为value的属性,而我们在Home.page中将它与一个外部对像userName举办绑定,在措施真正运行的时候,HTML模板中的user组件读取demo.Home这个JAVA类的userName属性,然后通过OGNL东西对Insert组件的value属性举办更新,最后user组件将更新后的值插入到模板傍边。好了,这时候你应该大白了Insert组件是奈何取值的吧。
需要强调的是Page组件是一种非凡的JWC组件,它可以包括其它的JWC组件,但不能被其它的JWC组件所包括。除此之外,Page组件尚有一些非凡的属性和成果,它也不能和其它组件举办组合而生成新的组件。
组件的定名方法和HTML模板的定名方法有些差异,它可以以.page作为扩展名(假如是Page组件),可能以.jwc作为扩展名(假如长短Page组件)。组件一般放在当前web应用的WEB-INF目次下面,以本例来说,我们的Home.page这个文件就应该放在webapps/Welcom/WEB-INF目次下面。
Home.java
package demo;
import org.apache.tapestry.html.BasePage;
public class Home extends BasePage {
private String userName = "Jack";
public String getUserName() {
return this.userName;
}
}
最后一部门即是我们的demo.Home这个JAVA类,它实际上就是一个普通的javaBean,独一差异的就是它必需从BasePage类中担任。它的浸染就是为HTML模板中的user组件提供文本信息的来历,在本例中我们简朴的返回”Jack”,但实际上getUserName要领还可以从JNDI、数据库、EJB中取得用户名。
#p#分页标题#e#
其实demo.Home不只仅只是提供数据来历,它还可以实现表单组件的提交。Tapestry提供了2个根基类利便用户举办扩充,一个是BasePage类(专用于Page组件),另一个是BaseComponent类(用于用户自界说组件)。通过担任这些组件,可以大大地减轻用户的编程量,从而把精神放到措施流程设计上去。
demo.Home组件类的名称应该与Home.page中界说的名称一致,它一般放在当前web应用的WEB-INF/classes目次下,以本例来说,Home.class这个类应该放在webapps/Welcome/WEB-INF/classes/demo目次下面。
Welcome.application
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<application name="Tapestry Illustration 1">
<page name="Home" specification-path="Home.page"/>
</application>
以上这个文件是这个Tapestry应用的设置文件,它是以application作为根元素的,个中page元素指定了一个名为Home的Page组件,它的文件名为Home.page,虽然你也可以指定更多的page组件,这个设置文件就像是一个总装车间,把一个个page组件或其它的组件装配起来。
关于这个设置文件的定名类型我们在前面已经讲过了,它一般放在WEB-INF目次下,在本例中它放在了webapps/Welcome/WEB-INF目次下面。
web.xml
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<display-name>Tapestry Welcome Application</display-name>
<servlet>
<servlet-name>Welcome</servlet-name>
<servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Welcome</servlet-name>
<url-pattern>/app</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>15</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
Listing 5. web.xml
这个文件相信所有的JSP/servlet开拓员都很是熟悉了吧?在这个文件中我们界说了一个名为Welcome的servlet,它实际上就是org.apache.tapestry.ApplicationServlet这个类,它的主要浸染是对客户的请求举办包装,然后转发到各个page组件举办处理惩罚,虽然假如你有非凡需要,也可以担任ApplicationServlet类(虽然这种环境很少呈现)。
个中<servlet-mapping>用来举办地点映射,相信servlet开拓员应该都知道它的浸染吧?我们把所有对付/app的请求全部转发给Welcome这个servlet举办处理惩罚,虽然你也可以将/app换成其你本身的界说。最后我们打开一个IE欣赏器,在地点栏中输入http://localhost:8080/Welcome/app 就会看到功效。
好了,此刻我们就讲一下关于上面谁人用户注册的例子吧。由于篇幅的干系,我就不把所有的设置文件一一摆列了,只摘录一些要害的设置。
CustInfo.html
<html jwcid="@Shell" title="Welcome Page">
<body jwcid="@Body">
<form jwcid="@Form" listener="ognl:listeners.submit">
Customer Name: <input jwcid="custName" type="text"/><br/>
Date-of-Birth: <input jwcid="dob" type="text" format="MMM dd, yyyy"/>
(Month DD, YYYY)<br/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
也许你看了上面这个文件不禁会问:为什么有的组件名称前面加了一个“@”?原因如下:tapestry由于是由浩瀚组件构成的,个中一般的组件都要在其Page组件顶用<component>元素举办界说,对付一些简朴的可能无其它附加参数的组件来说就显得较量贫苦,因此tapestry提出了显式界说组件和隐式界说组件这个观念,显式界说是指明晰地在Page组件中界说过的组件,隐式界说是指没有明晰地在Page组件中界说的组件,都要在组件名字前加一个“@”。
这里有一个组件显得很出格:@Form组件,这个组件由于是隐式界说地,因此它的参数就直接在HTML模板中举办界说:listener="ognl:listeners.submit",它暗示当用户按下submit按钮后页面流程会交给当前Page组件类(即Welcome这个类)的submit函数举办打点。
下面我们再看一下它的Page组件的界说:
CustInfo.page
#p#分页标题#e#
<?xml version="1.0" encoding="UTF-8"?>
<!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="demo.Welcome">
<property-specification name="custName" type="java.lang.String"/>
<property-specification name="dob" type="java.util.Date"/>
<component id="custName" type="TextField">
<binding name="value" expression="custName"/>
</component>
<component id="dob" type="DatePicker">
<binding name="value" expression="dob"/>
</component>
</page-specification>
个中值得存眷的是dob这个组件,它的范例是tapestry焦点组件库中的DatePicker组件,有了它,我们就可以生成先前谁人不行思义的选择日期的界面了。
这里呈现了一个新面目:property-specification元素,它是干什么用的呢?照旧先让我们看一下谁人“hello,world”例子中的组件类的界说吧:
Home.java
package demo;
import org.apache.tapestry.html.BasePage;
public class Home extends BasePage {
private String userName = "Jack";
public String getUserName() {
return this.userName;
}
}
个中userName这个属性是一个javaBean属性,通过get或set要领来存取userName的值,虽然对付本例,property-specification这个元素和它一样,就是界说了一个javaBean的属性,其实你也可以像上例那样举办存取,而不需在CustInfo.page中举办界说。界说property-specification元素的基础原因其实很好笑:为了偷一点懒! 为什么这么说呢?因为假如你用property-specification元素来界说javaBean的属性的话,那你在组件类中就不必实现其get或set要领了,只需用一个抽像要领来完成,至于真正的get或set要领的实现就由tapestry来代庖了,个中,Welcome这个类的代码如下:
Welcome.java
package demo;
import java.util.Date;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;
public abstract class Welcome extends BasePage {
public abstract void setCustName(String custName);
public abstract void setDob(Date dob);
public abstract String getCustName();
public abstract Date getDob();
public void submit(IRequestCycle cycle) {
if (getCustName() != null
&& !getCustName().trim().equals("")
&& getDob() != null) {
Welcome welcome = (Welcome) cycle.getPage("Welcome");
welcome.setCustName(getCustName());
welcome.setDob(getDob());
cycle.activate(welcome);
}
}
}
前面的几个抽像要领就不多说了,Welcome类的submit要领和在HTML模板中界说的@Form组件中的listener参数正好对应,也就是说,当用户按下提交按钮后,这个submit要领就会被激活,它进一步地引导着页面下一步的行动。在本例中它先查抄用户名和用户出生日期(DOB,date of birthday的简称)是否为空,假如不为空,就将用户在页面中输入的值赋予Welocome这个类的custName和dob这两个属性,然后激活welcome这个Page组件,也就是将页面跳转到welcome这个Page上去。
需要留意的是,Form组件界说的监听类要领必需为public,而且要带一个IRequestCycle 的参数,IrequestCycle是一个接口,它是由tapestry提供的一个对付用户而言,当前会话的一个打点东西。
总结
到今朝为止,你或许大白了Tapestry的道理了,可是一个巨大的Tapestry应用照旧要思量到许多问题的,好比页面的界说,页面之间的流程,组件元素的耐久性设计,与EJB或原有的JSP系统的集成等等问题,可是Tapestry都已经为你思量好了,你甚至可以把它的源码下载下来仔细研究,也可以到它的邮件列表上颁发本身的概念,这正是我所喜爱的—-open source ! 它代表着自由!