介紹Tapestry的相關中文網址:
http://www.netbei.com/Article/jsp/Tapestry/Index.html
Tapestry是一個開源的基于servlet的應用程序框架,它使用組件對象模型來創建動態的,交互的web應用。一個組件就是任意一個帶有jwcid屬性的html標記。其中jwc的意思是Java Web Component。Tapestry使得java代碼與html完全分離,利用這個框架開發大型應用變得輕而易舉。并且開發的應用很容易維護和升級。Tapestry支持本地化,其錯誤報告也很詳細。Tapestry主要利用javabean和xml技術進行開發。
第一個應用程序
在介紹第一個應用之前,先介紹一下Tapestry的安裝。從sourceforge下載其最新版,解壓后,將lib目錄下的jar文件放到CLASSPATH中,將其中的war文件放到tomcat的webapp目錄下。然后就可以通過http://localhost:8080/tutorial訪問其tutorial應用。
在Tapestry中一個應用程序有以下幾部分組成,我們以其自身帶的HelloWorld程序為例介紹:
Servlet:
這是一個應用的主體部分:servlet類,這個類必須是ApplicationServlet的子類,并且必須實現getApplicationSpecificationPath()方法。示例如下:
import com.primix.tapestry.*;
public class HelloWorldServlet extends ApplicationServlet
{
protected String getApplicationSpecificationPath()
{
return "/tutorial/hello/HelloWorld.application";
}
}
/tutorial/hello/HelloWorld.application是一個應用的說明文件。
Application Specification:
其實就是描述這個應用的一個xml文件,在這個應用中有許多參數需要設置,engine-class將在下面介紹,page中的name屬性指定html文件名,specification-path指定對這個頁面的說明文件。在一個應用中可以有很多個page,但必須有一個page的name為"Home",因為當訪問你的應用時,首先顯示的就是這個page。
<?xml version="1.0" encoding="UTF-8"?>;
<!DOCTYPE application PUBLIC "-//Howard Ship//Tapestry Specification 1.1//EN" "http://tapestry.sf.net/dtd/Tapestry_1_1.dtd">;
<application name="Hello World Tutorial" engine-class="com.primix.tapestry.engine.SimpleEngine">;
<page name="Home" specification-path="/tutorial/hello/Home.jwc"/>;
</application>;
Application Engine:
當客戶連接到Tapestry應用時,Tapestry將會創建一個Engine對象(類似于session)。通常我們程序中的application engine 一般是SimpleEngine類的一個實例,當然這個類的子類也可以。
Page Specification:
跟應用說明相似,頁說明也是一個xml描述文件:
<?xml version="1.0" encoding="UTF-8"?>;
<!DOCTYPE specification PUBLIC "-//Howard Ship//Tapestry Specification 1.1//EN" "http://tapestry.sf.net/dtd/Tapestry_1_1.dtd">;
<specification class="com.primix.tapestry.BasePage"/>;
因為這個應用是靜態的,所以使用com.primix.tapestry.BasePage即可,如果是動態的應用,則需在這個文件中定義一些component,當然使用BasePage為基類的派生類也可以。
html頁面:
這個應用的html頁面非常簡單:
<html>;
<head>;
<title>;Hello World</title>;
</head>;
<body>;
<b>;HelloWorld</b>;
</body>;
</html>;
注意上面所講到的各種文件都要放到放在WAR的WEB-INF/classes目錄下。
一個復雜的應用
在這個應用中我們以一個簡單的學生管理系統為例介紹一下Tapestry的常用功能。我們要實現學生的增加和顯示,因此我們需要兩個html頁面。至于StudentServlet類和Student.application我們就不描述了,在Student.application中定義了兩個page:Home和EditStudent,具體看附件。學生數據存放在數據庫中,我們用Student類表示數據中的一條記錄,用StudentFactory類檢索學生數據,這兩個類用到了一個JDBC包裝器,關于這個JDBC包裝器可以見我的另外一篇文章<<對一個簡單的 JDBC 包裝器的擴展及應用>;>;。
首先看一下Home.html
<html>;
<head>;
<title>;學生管理</title>;
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">;
</head>;
<body bgcolor="#FFFFFF">;
<p align="center">;學生列表</p>;
<table width="100%" border="1">;
<tr>;
<td >;學號</td>;
<td >;姓名</td>;
<td >;性別</td>;
<td >;班級</td>;
</tr>;
<span jwcid="liststudent">;
<tr>;
<td>;<span jwcid="id">;20012400</span>;</td>;
<td>;<span jwcid="sname">;宗鋒</span>;</td>;
<td>;<span jwcid="gender">;男</span>;</td>;
<td>;<span jwcid="department">;計算機研一</span>;</td>;
</tr>;
</span>;
<tr jwcid="$remove$">;
<td>;20011389</td>;
<td>;桑一珊</td>;
<td>;男</td>;
<td>;計算機研一</td>;
</tr>;
</table>;
<a jwcid="add">;添加學生</a>;
</body>;
</html>;
與前面的簡單應用不同,我們在這個頁面中定義了七個組件,下面看一下部分Home.jwc文件,我們將詳細講述一下怎樣描述這些組件。
<specification class="test.ListStudent">;
<component id="liststudent" type="Foreach">;
<binding name="source" property-path="student"/>;
<binding name="value" property-path="eachstudent"/>;
</component>;
<component id="id" type="Insert">;
<binding name="value" property-path="eachstudent.id"/>;
</component>;
<component id="add" type="Page">;
<static-binding name="page">;EditStudent</static-binding>;
</component>;
</specification>;
在這里,我們的specification的class屬性值不再是BasePage,而是其派生類ListStudent。對于每一個組件,id屬性指定唯一的標識符,這個值與html文件中的jwcid值相對應,type 指定組件名,binding指定組件怎得到數據,property-path是一系列屬性的集合,這些屬性一般定義在javabean中,例如上面的property-path="student",則在相應的javabean類ListStudent中應該有個函數getStudent。liststudent是一個Foreach組件,這個組件其實是一個for循環,它從source中讀入一個數組,將其一個一個的賦值給value參數指定的屬性。id,name,gender,department四個是Insert組件,這個組件用來插入文本數據,參數value指定要插入的值,property-path指定怎樣獲取這些值,eachstudent.id相當于調用javabean的getEachstudent().getId()。add是一個Page組件,page屬性指定頁面名(定義在application文件中),static-binding表明要綁定的數據是不可修改的。$remove$組件沒有在這個文件中描述,因為Tapestry運行時會自動刪除這種組件。
下面看一下ListStudent類:
package test;
import com.primix.tapestry.*;
import sun.jdbc.odbc.JdbcOdbcDriver ;
/**
* 返回每個學生的數據
*
*/
public class ListStudent extends BasePage
{
private Student eachstudent;
private Student[] student;
public void detach()
{
eachstudent=null;
student=null;
super.detach();
}
public Student getEachstudent()
{
return eachstudent;
}
public void setEachstudent(Student value)
{
eachstudent = value;
}
public Student[] getStudent()
{
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
student=StudentFactory.findAllStudents();
}catch(Exception e){
e.printStackTrace();
}
return student;
}
}
這個類有四個函數,其中detach函數是將頁面放入緩沖池時執行的操作,getStudent函數返回所有的學生記錄,這是給jwc文件中liststudent組件的source參數賦值,getEachstudent給這個組件的value參數賦值,因為source是一個數組,每次循環需要從中取出一條記錄賦值給eachstudent,所以還有一個函數為setEachstudent,你會注意到這個函數很簡單,其實是Tapestry幫你做了大部分工作。
至此,顯示學生的部分已經完成,下面看一下EditStudent.html
<html>;
<head>;
<title>;增加學生</title>;
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">;
</head>;
<body>;
<p>;<img src="student.gif" width="32" height="32"/>; 學生管理系統</p>;
<form jwcid="form">;
<span jwcid="ifError">;
<font size=+2 color=red>;<span jwcid="insertError"/>;</font>;
</span>;
<p>;學號:
<input jwcid="id"/>;
</p>;
<p>;姓名:
<input jwcid="name"/>;
</p>;
<span jwcid="gender">;
<p>;性別:
<input jwcid="male"/>;
男
<input jwcid="female"/>;
女
</p>;
</span>;
<p>;班級:
<input jwcid="department"/>;
</p>;
<p>;
<input type="submit" value="確定">;
</p>;
</form>;
</body>;
</html>;
在這個文件中,用到了另外一些常用的組件,先看一下EditStudent.jwc中的這些組件的描述:
<specification class="test.EditStudent">;
<component id="form" type="Form">;
<binding name="listener" property-path="listeners.formSubmit"/>;
</component>;
<component id="gender" type="RadioGroup">;
<binding name="selected" property-path="gender"/>;
</component>;
<component id="ifError" type="Conditional">;
<binding name="condition" property-path="error"/>;
</component>;
<component id="insertError" type="Insert">;
<binding name="value" property-path="error"/>;
</component>;
<component id="id" type="TextField">;
<binding name="value" property-path="id"/>;
</component>;
<component id="male" type="Radio">;
<field-binding name="value" field-name="test.EditStudent.MALE"/>;
</component>;
</specification>;
form是一個Form組件,它的參數listener指定submit這個form時有那個函數處理。ifError是一個Conditional組件,這個組件指定當condition滿足時才會顯示,在本例中,如果error不為空,則condition滿足。在這個組件中,有嵌套了一個Insert類型的組件,用于將錯誤顯示。這是Tapestry中經常用到的處理錯誤的方式。gender是一個RadioGroup組件,它綁定了javabean中的gender屬性,selected參數指定那個radio被選中,在這個組件中,又嵌套了兩個Radio組件,分別用來表示男,女。Radio的value參數指定當用戶選定這個radio時,RadioGroup綁定的屬性值將會等于field-name中指定的值(這個值必須是static的),在本例中,gender=test.EditStudent.MALE。id是一個TextField組件,其參數value綁定到javabean中的id屬性。
下面是相應的EditStudent類:
package test;
import com.primix.tapestry.*;
public class EditStudent extends BasePage
{
public static final int MALE = 1;
public static final int FEMALE = 2;
private int gender;
private String error;
private String id;
private String sname;
private String department;
public void detach()
{
error = null;
id=null;
sname=null;
gender=0;
department=null;
super.detach();
}
public int getGender()
{
return gender;
}
public String getId()
{
return id;
}
public String getSname()
{
return sname;
}
public String getDepartment()
{
return department;
}
public void setGender(int value)
{
gender = value;
fireObservedChange("gender", value);
}
public void setId(String value)
{
id = value;
fireObservedChange("id", value);
}
public String getError()
{
return error;
}
public void setSname(String value)
{
sname = value;
fireObservedChange("sname", value);
}
public void setDepartment(String value)
{
department = value;
fireObservedChange("department", value);
}
public void formSubmit(IRequestCycle cycle)
{
//判斷用戶是否添完了所有數據
if (gender== 0||id==null||id.equals("")||sname==null||sname.equals("")||
department==null||department.equals(""))
{
error = "請填充完所有選項";
return;
}
//將學生保存
try{
Student student=new Student();
student.setId(id);
student.setName(sname);
if(gender==1)
student.setGender("男");
else
student.setGender("女");
student.setDepartment(department);
student.save(null);
}catch(Exception e){
e.printStackTrace();
}
//清空當前的各個屬性,以免再次進入此頁面時,各屬性仍舊保留原來的值
setSname(null);
setDepartment(null);
setId(null);
setGender(0);
//重定向到Home頁面
cycle.setPage("Home");
}
}
在本類的一些設置屬性的函數中使用了fireObservedChange這個函數,這個函數激發一個改變事件,通知當前的屬性的值已經改變。
其他應用
Tapestry中自帶的例子中的Workbench中的localization例子演示了怎樣使用本地化,你只需要創建不同語言的html模板,還有圖形等其它一些html中用到的資源。例如創建一個法語版的EditStudent.html,則相應的html文件名為EditStudent_fr.html,而jwc中定義的組件的描述不用有多個版本。這里要介紹一下Tapestry本地化中經常用到的一個概念:assets。assets是一些web應用中用到的資源,如圖象,視頻。assets有三種:external, internal 和private。External類型的assets來源于任意的URL。Internal類型的assets來源于和Tapestry應用在同一個服務器上的URL。Private 類型的assets允許部署在WAR的WEB-INF/classes目錄下(同上面的html模板,jwc文件一樣),這個目錄對于web服務器來說是不可見的。
看一下Workbench中localization例子中的localization.jwc文件的片斷:
<component id="changeButton" type="ImageSubmit">;
<binding name="image" property-path="assets.change-button"/>;
</component>;
<private-asset name="change-button" resource-path="/tutorial/workbench/localization/Change.gif"/>;
在changeButton組件中就使用了private assets,而這些圖像文件就放在WAR的WEB-INF/classes下,注意圖像跟html一樣也有多個語言的版本。
注意jwc文件中的inputLocale這個組件,其實localization應用就是通過這個組件來實現本地化。具體參數請看其Developer guide。
<component id="inputLocale" type="PropertySelection">;
<binding name="value" property-path="page.engine.locale"/>;
<binding name="model" property-path="localeModel"/>;
</component>;
Tapestry還支持創建自己的可重用組件,其自身帶了一個這樣的例子:Border。同時它還有其它一些例子:Inspector展示了怎樣監視你的應用程序。vlib是一個用tapestry作表示層的j2ee應用程序(用jboss作為應用服務器)。
Tapestry的功能非常強大,本文只是介紹了其一小部分,還有很多方面沒有涉及到,例如javascript在Tapestry中的應用。具體可以看其文檔,相信如果你用一下這個框架,你就會被它深深吸引。Tapestry的文檔做的不是很全,不過經過不斷的摸索,相信你會很快掌握它。
http://www.netbei.com/Article/jsp/Tapestry/Index.html
Tapestry是一個開源的基于servlet的應用程序框架,它使用組件對象模型來創建動態的,交互的web應用。一個組件就是任意一個帶有jwcid屬性的html標記。其中jwc的意思是Java Web Component。Tapestry使得java代碼與html完全分離,利用這個框架開發大型應用變得輕而易舉。并且開發的應用很容易維護和升級。Tapestry支持本地化,其錯誤報告也很詳細。Tapestry主要利用javabean和xml技術進行開發。
第一個應用程序
在介紹第一個應用之前,先介紹一下Tapestry的安裝。從sourceforge下載其最新版,解壓后,將lib目錄下的jar文件放到CLASSPATH中,將其中的war文件放到tomcat的webapp目錄下。然后就可以通過http://localhost:8080/tutorial訪問其tutorial應用。
在Tapestry中一個應用程序有以下幾部分組成,我們以其自身帶的HelloWorld程序為例介紹:
Servlet:
這是一個應用的主體部分:servlet類,這個類必須是ApplicationServlet的子類,并且必須實現getApplicationSpecificationPath()方法。示例如下:
import com.primix.tapestry.*;
public class HelloWorldServlet extends ApplicationServlet
{
protected String getApplicationSpecificationPath()
{
return "/tutorial/hello/HelloWorld.application";
}
}
/tutorial/hello/HelloWorld.application是一個應用的說明文件。
Application Specification:
其實就是描述這個應用的一個xml文件,在這個應用中有許多參數需要設置,engine-class將在下面介紹,page中的name屬性指定html文件名,specification-path指定對這個頁面的說明文件。在一個應用中可以有很多個page,但必須有一個page的name為"Home",因為當訪問你的應用時,首先顯示的就是這個page。
<?xml version="1.0" encoding="UTF-8"?>;
<!DOCTYPE application PUBLIC "-//Howard Ship//Tapestry Specification 1.1//EN" "http://tapestry.sf.net/dtd/Tapestry_1_1.dtd">;
<application name="Hello World Tutorial" engine-class="com.primix.tapestry.engine.SimpleEngine">;
<page name="Home" specification-path="/tutorial/hello/Home.jwc"/>;
</application>;
Application Engine:
當客戶連接到Tapestry應用時,Tapestry將會創建一個Engine對象(類似于session)。通常我們程序中的application engine 一般是SimpleEngine類的一個實例,當然這個類的子類也可以。
Page Specification:
跟應用說明相似,頁說明也是一個xml描述文件:
<?xml version="1.0" encoding="UTF-8"?>;
<!DOCTYPE specification PUBLIC "-//Howard Ship//Tapestry Specification 1.1//EN" "http://tapestry.sf.net/dtd/Tapestry_1_1.dtd">;
<specification class="com.primix.tapestry.BasePage"/>;
因為這個應用是靜態的,所以使用com.primix.tapestry.BasePage即可,如果是動態的應用,則需在這個文件中定義一些component,當然使用BasePage為基類的派生類也可以。
html頁面:
這個應用的html頁面非常簡單:
<html>;
<head>;
<title>;Hello World</title>;
</head>;
<body>;
<b>;HelloWorld</b>;
</body>;
</html>;
注意上面所講到的各種文件都要放到放在WAR的WEB-INF/classes目錄下。
一個復雜的應用
在這個應用中我們以一個簡單的學生管理系統為例介紹一下Tapestry的常用功能。我們要實現學生的增加和顯示,因此我們需要兩個html頁面。至于StudentServlet類和Student.application我們就不描述了,在Student.application中定義了兩個page:Home和EditStudent,具體看附件。學生數據存放在數據庫中,我們用Student類表示數據中的一條記錄,用StudentFactory類檢索學生數據,這兩個類用到了一個JDBC包裝器,關于這個JDBC包裝器可以見我的另外一篇文章<<對一個簡單的 JDBC 包裝器的擴展及應用>;>;。
首先看一下Home.html
<html>;
<head>;
<title>;學生管理</title>;
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">;
</head>;
<body bgcolor="#FFFFFF">;
<p align="center">;學生列表</p>;
<table width="100%" border="1">;
<tr>;
<td >;學號</td>;
<td >;姓名</td>;
<td >;性別</td>;
<td >;班級</td>;
</tr>;
<span jwcid="liststudent">;
<tr>;
<td>;<span jwcid="id">;20012400</span>;</td>;
<td>;<span jwcid="sname">;宗鋒</span>;</td>;
<td>;<span jwcid="gender">;男</span>;</td>;
<td>;<span jwcid="department">;計算機研一</span>;</td>;
</tr>;
</span>;
<tr jwcid="$remove$">;
<td>;20011389</td>;
<td>;桑一珊</td>;
<td>;男</td>;
<td>;計算機研一</td>;
</tr>;
</table>;
<a jwcid="add">;添加學生</a>;
</body>;
</html>;
與前面的簡單應用不同,我們在這個頁面中定義了七個組件,下面看一下部分Home.jwc文件,我們將詳細講述一下怎樣描述這些組件。
<specification class="test.ListStudent">;
<component id="liststudent" type="Foreach">;
<binding name="source" property-path="student"/>;
<binding name="value" property-path="eachstudent"/>;
</component>;
<component id="id" type="Insert">;
<binding name="value" property-path="eachstudent.id"/>;
</component>;
<component id="add" type="Page">;
<static-binding name="page">;EditStudent</static-binding>;
</component>;
</specification>;
在這里,我們的specification的class屬性值不再是BasePage,而是其派生類ListStudent。對于每一個組件,id屬性指定唯一的標識符,這個值與html文件中的jwcid值相對應,type 指定組件名,binding指定組件怎得到數據,property-path是一系列屬性的集合,這些屬性一般定義在javabean中,例如上面的property-path="student",則在相應的javabean類ListStudent中應該有個函數getStudent。liststudent是一個Foreach組件,這個組件其實是一個for循環,它從source中讀入一個數組,將其一個一個的賦值給value參數指定的屬性。id,name,gender,department四個是Insert組件,這個組件用來插入文本數據,參數value指定要插入的值,property-path指定怎樣獲取這些值,eachstudent.id相當于調用javabean的getEachstudent().getId()。add是一個Page組件,page屬性指定頁面名(定義在application文件中),static-binding表明要綁定的數據是不可修改的。$remove$組件沒有在這個文件中描述,因為Tapestry運行時會自動刪除這種組件。
下面看一下ListStudent類:
package test;
import com.primix.tapestry.*;
import sun.jdbc.odbc.JdbcOdbcDriver ;
/**
* 返回每個學生的數據
*
*/
public class ListStudent extends BasePage
{
private Student eachstudent;
private Student[] student;
public void detach()
{
eachstudent=null;
student=null;
super.detach();
}
public Student getEachstudent()
{
return eachstudent;
}
public void setEachstudent(Student value)
{
eachstudent = value;
}
public Student[] getStudent()
{
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
student=StudentFactory.findAllStudents();
}catch(Exception e){
e.printStackTrace();
}
return student;
}
}
這個類有四個函數,其中detach函數是將頁面放入緩沖池時執行的操作,getStudent函數返回所有的學生記錄,這是給jwc文件中liststudent組件的source參數賦值,getEachstudent給這個組件的value參數賦值,因為source是一個數組,每次循環需要從中取出一條記錄賦值給eachstudent,所以還有一個函數為setEachstudent,你會注意到這個函數很簡單,其實是Tapestry幫你做了大部分工作。
至此,顯示學生的部分已經完成,下面看一下EditStudent.html
<html>;
<head>;
<title>;增加學生</title>;
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">;
</head>;
<body>;
<p>;<img src="student.gif" width="32" height="32"/>; 學生管理系統</p>;
<form jwcid="form">;
<span jwcid="ifError">;
<font size=+2 color=red>;<span jwcid="insertError"/>;</font>;
</span>;
<p>;學號:
<input jwcid="id"/>;
</p>;
<p>;姓名:
<input jwcid="name"/>;
</p>;
<span jwcid="gender">;
<p>;性別:
<input jwcid="male"/>;
男
<input jwcid="female"/>;
女
</p>;
</span>;
<p>;班級:
<input jwcid="department"/>;
</p>;
<p>;
<input type="submit" value="確定">;
</p>;
</form>;
</body>;
</html>;
在這個文件中,用到了另外一些常用的組件,先看一下EditStudent.jwc中的這些組件的描述:
<specification class="test.EditStudent">;
<component id="form" type="Form">;
<binding name="listener" property-path="listeners.formSubmit"/>;
</component>;
<component id="gender" type="RadioGroup">;
<binding name="selected" property-path="gender"/>;
</component>;
<component id="ifError" type="Conditional">;
<binding name="condition" property-path="error"/>;
</component>;
<component id="insertError" type="Insert">;
<binding name="value" property-path="error"/>;
</component>;
<component id="id" type="TextField">;
<binding name="value" property-path="id"/>;
</component>;
<component id="male" type="Radio">;
<field-binding name="value" field-name="test.EditStudent.MALE"/>;
</component>;
</specification>;
form是一個Form組件,它的參數listener指定submit這個form時有那個函數處理。ifError是一個Conditional組件,這個組件指定當condition滿足時才會顯示,在本例中,如果error不為空,則condition滿足。在這個組件中,有嵌套了一個Insert類型的組件,用于將錯誤顯示。這是Tapestry中經常用到的處理錯誤的方式。gender是一個RadioGroup組件,它綁定了javabean中的gender屬性,selected參數指定那個radio被選中,在這個組件中,又嵌套了兩個Radio組件,分別用來表示男,女。Radio的value參數指定當用戶選定這個radio時,RadioGroup綁定的屬性值將會等于field-name中指定的值(這個值必須是static的),在本例中,gender=test.EditStudent.MALE。id是一個TextField組件,其參數value綁定到javabean中的id屬性。
下面是相應的EditStudent類:
package test;
import com.primix.tapestry.*;
public class EditStudent extends BasePage
{
public static final int MALE = 1;
public static final int FEMALE = 2;
private int gender;
private String error;
private String id;
private String sname;
private String department;
public void detach()
{
error = null;
id=null;
sname=null;
gender=0;
department=null;
super.detach();
}
public int getGender()
{
return gender;
}
public String getId()
{
return id;
}
public String getSname()
{
return sname;
}
public String getDepartment()
{
return department;
}
public void setGender(int value)
{
gender = value;
fireObservedChange("gender", value);
}
public void setId(String value)
{
id = value;
fireObservedChange("id", value);
}
public String getError()
{
return error;
}
public void setSname(String value)
{
sname = value;
fireObservedChange("sname", value);
}
public void setDepartment(String value)
{
department = value;
fireObservedChange("department", value);
}
public void formSubmit(IRequestCycle cycle)
{
//判斷用戶是否添完了所有數據
if (gender== 0||id==null||id.equals("")||sname==null||sname.equals("")||
department==null||department.equals(""))
{
error = "請填充完所有選項";
return;
}
//將學生保存
try{
Student student=new Student();
student.setId(id);
student.setName(sname);
if(gender==1)
student.setGender("男");
else
student.setGender("女");
student.setDepartment(department);
student.save(null);
}catch(Exception e){
e.printStackTrace();
}
//清空當前的各個屬性,以免再次進入此頁面時,各屬性仍舊保留原來的值
setSname(null);
setDepartment(null);
setId(null);
setGender(0);
//重定向到Home頁面
cycle.setPage("Home");
}
}
在本類的一些設置屬性的函數中使用了fireObservedChange這個函數,這個函數激發一個改變事件,通知當前的屬性的值已經改變。
其他應用
Tapestry中自帶的例子中的Workbench中的localization例子演示了怎樣使用本地化,你只需要創建不同語言的html模板,還有圖形等其它一些html中用到的資源。例如創建一個法語版的EditStudent.html,則相應的html文件名為EditStudent_fr.html,而jwc中定義的組件的描述不用有多個版本。這里要介紹一下Tapestry本地化中經常用到的一個概念:assets。assets是一些web應用中用到的資源,如圖象,視頻。assets有三種:external, internal 和private。External類型的assets來源于任意的URL。Internal類型的assets來源于和Tapestry應用在同一個服務器上的URL。Private 類型的assets允許部署在WAR的WEB-INF/classes目錄下(同上面的html模板,jwc文件一樣),這個目錄對于web服務器來說是不可見的。
看一下Workbench中localization例子中的localization.jwc文件的片斷:
<component id="changeButton" type="ImageSubmit">;
<binding name="image" property-path="assets.change-button"/>;
</component>;
<private-asset name="change-button" resource-path="/tutorial/workbench/localization/Change.gif"/>;
在changeButton組件中就使用了private assets,而這些圖像文件就放在WAR的WEB-INF/classes下,注意圖像跟html一樣也有多個語言的版本。
注意jwc文件中的inputLocale這個組件,其實localization應用就是通過這個組件來實現本地化。具體參數請看其Developer guide。
<component id="inputLocale" type="PropertySelection">;
<binding name="value" property-path="page.engine.locale"/>;
<binding name="model" property-path="localeModel"/>;
</component>;
Tapestry還支持創建自己的可重用組件,其自身帶了一個這樣的例子:Border。同時它還有其它一些例子:Inspector展示了怎樣監視你的應用程序。vlib是一個用tapestry作表示層的j2ee應用程序(用jboss作為應用服務器)。
Tapestry的功能非常強大,本文只是介紹了其一小部分,還有很多方面沒有涉及到,例如javascript在Tapestry中的應用。具體可以看其文檔,相信如果你用一下這個框架,你就會被它深深吸引。Tapestry的文檔做的不是很全,不過經過不斷的摸索,相信你會很快掌握它。