Tapestry 5
组件类
Tapestry 5
的组件类要比Tapestry 4
简单些,它们没有要继承的基类、是具体而非抽象的且没有XML
文件,不过还是存在少量的以Java annotations
形式的配置,但现在那些都直接声明在类的属性上,优于声明在抽象的getters
和settes
方法上(Tapestry 4
中)。
页面、组件和组件混入类都以同种方式创建。
在Tapestry 5
中创建页面和组件轻而易举!
不同于Tapestry 4
,Tapestry 5
中组件类不再是抽象的且不继承框架的基类。它们是纯粹的POJOs
(Plain Old Java Objects
)。
仅具有少量约束:
§
类必须声明是公共的(public
)。
§
类必须具有一个标准公
共
的无参构造器。
以下是一个非常基础的组件:
java 代码
- package org.example.myapp.components;
-
- import org.apache.tapestry.MarkupWriter;
- import org.apache.tapestry.annotations.BeginRender;
-
- public class HelloWorld
- {
- @BeginRender
- void renderMessage(MarkupWriter writer)
- {
- writer.write("Bonjour from HelloWorld component.");
- }
- }
这个组件仅用来输出一个固定的信息。
BeginRender annotation
是一个组件生命周期annotation
,用来告诉Tapestry
什么时候在什么情况下调用你类中的方法。
还有一点与Tapestry 4
不同的是,方法不再需要声明为公共的
(public
),可以具有你想要的任何可见性。
组件包
组件类必须存放在一个相应的包中(运行时代码转换和类重载的需要)。
这些包存放在应用根目录
${root}
包下。
页面类放置在
${root}.
pages
下,页面名匹配于这个包下的类名。
组件类放置在
${root}.
components
下,组件类型(组件模板中标签属性t:type
的引用)匹配于这个包下的类名。
混入(mixins
)类放置在
${root}.
mixins
下,Mixin
类型匹配于这个包下的类名。
另外,整个应用存在基类很常见,通常是一些抽象的基类,不需要直接被引用。这些不应直接放在
pages
、components
、mixins
包下,因为它们这时看起来是有效的pages
、components
、mixins
,我们用
${root}.
base
包来存放这些基类。
子文件夹/
子包
类不必直接放入
${root}
包下(pages
、components
、mixins
等等),可以创建子包来存放一些类。子包名将成为页面名或组件类型的一部分。因此你可以定义页面组件com.example.myapp.pages.admin.CreateUser
,与此同时页面逻辑名(显示在URL
上的)将会是
admin/CreateUser
。
页面与组件
在Tapestry 5
中,页面与组件的区别已经非常小了。实际上仅有的不同就是包名:
${root}.
pages.
PageName
用于页面,
${root}.
components.
ComponentType
用于组件。
Tapestry 4
中,页面与组件的区别很大,暴露出分隔的接口以及用于编码继承的抽象实现体系。
Tapestry 5
中,页面仍用于表现,但它已真正成为Tapestry
的内部(internal
)类。页面组件仅仅是页面组件树的根组件。
类转换
Tapestry
以使用你的类做为出发点,在运行时对类进行转换,其原因就包括Tapestry
如何在多个请求中缓存(pools
)页面。
For the most part, these transformations are both sensible and invisible. In a few limited cases, they are maginally leaky -- for instance, the requirement that instance variables be private -- but we feel that the programming model in general will support very high levels of developer productivity.
类转换多半是合理的且不可见的,少数受限的情况下,它们的被忽略无关紧要——比如,一个实例需要是私有的——但是我们会发现设计模式通常会支持一个更高的开发效率。
因为转换发生在运行时,所以转换时并不会影响你实际中创建的Tapestry
应用。此外,测试时你的类完全是简单的POJOs
。
类重载
Component classes are monitored for changes by the framework. Classes are reloaded when changed. This allows you to build your application with a speed approaching that of a scripting environment, without sacrificing any of the power of the Java platform.
实例变量
Tapestry
组件可以包含实例变量(不同于Tapestry 4
,你得使用抽象属性)。
实例变量必须是私有的
。Tapestry
将会执行运行时类的修改来支持实例变量,而且仅对私有变量有效。类中可以有非私有的变量,由于Tapestry
池化和重新使用页面与组件,你这时会看到应用中的意外行为。对含有非静态且非私有的属性变量,Tapestry
将输出一个错误日志。
注意你需要提供getter
和settter
方法访问你类的实例变量。Tapestry
不会自动做这事。
瞬态实例变量
除非你的实例变量被annotation
装饰,否则它就是瞬态的实例变量。这意味着它们的值在每一个请求结束时(当页面从请求中分离时)被重置为默认的值。
假如你的变量能够在多个请求中保持它的值,你就需要在变量上使用
Retain annotation
来打破这一重置的规则。你需要注意的是没有客户相关的数据存入在这个属性变量里,因为后面的请求,相同页面实例可能被不同用户所使用。与此同时,相同用户后来的发出请求,也可能是不同的页面实例被使用。
此外,final
属性变量不会被重置。
构造器
Tapestry
将会使用默认的无参构造器来实例化你的类。其它构造器将被忽略。
注入
依赖注入(
Injection
)通过附加的annotations
发生在属性级别。在运行时阶段,属性包含的注入将变为只读性的。
参数
持久化属性
属性可以被加上annotation
以便它们在跨请求中保持值。
内嵌的组件
组件通常包含其他组件,组件内置在其他组件里时就叫做内嵌的组件。包含内嵌组件的组件,其模板将包含<comp></comp>
元素来识别内嵌组件放置在何处。
我们可以在模板中定义组件的类型,或者使用
Component annotation
定义组件类型与参数来创建组件实例变量,对于后者你不能提供<comp></comp>
元素的类型属性,在模板中只提供id
属性就行(假如你提供类型属性的话,Tapestry
将会使用使用被annotation
声明的属性的类型并输出一个错误日志)。
Example:
java 代码
- package org.example.app.pages;
-
- import org.apache.tapestry.annotations.Component;
- import org.example.app.components.Count;
-
- public class Countdown
- {
- @Component(parameters =
- { "start=5", "end=1", "value=countValue" })
- private Count _count;
-
- private int _countValue;
-
- public int getCountValue()
- {
- return _countValue;
- }
-
- public void setCountValue(int countValue)
- {
- _countValue = countValue;
- }
- }
以上定义的内嵌组件id
为"count"
(此id
源自属性的名字)。组件的类型是org.example.app.components.Count
。Count
组件的参数start
和end
绑定了字面值(literal values
),value
参数绑定了Countdown
组件的countValue
属性。
需要注意的是组件类里默认的绑定前缀是"prop:"
,以上实例中我们可以写成"value=prop:countValue"
来完整的显示绑定。
然而,某些字面值,比如上例中的数字字面值被接受为prop:
前缀,即使他们并不是真正的属性(这给应用开发者带来了极大的方便)。我们也可以使用"literal:"
前缀,"start=literal:5"
可以完成同样的事情。
我们可以在组件模板中指定其他的参数,但是组件类里的参数优先。
TODO: May want a more complex check; what if user uses prop: in the template and there's a conflict?
我们可以利用Component annotation
的id
属性覆盖默认的组件id
(即属性名)。
如果在组件类中定义一个组件,而模板中没有相应的<comp></comp>
元素,Tapestry
会输出一个错误日志。