原文:
http://tapestry.apache.org/exploring-the-project.html
通过maven,该项目得以有一个合理的代码布局
让我们看看maven从原型中创建的东西,从web.xml开始了解。
src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>tutorial1 Tapestry 5 Application</display-name> <context-param> <!-- The only significant configuration for Tapestry 5, this informs Tapestry of where to look for pages, components and mixins. --> <param-name>tapestry.app-package</param-name> <param-value>com.example.tutorial</param-value> </context-param> <!-- Specify some additional Modules for two different execution modes: development and qa. Remember that the default execution mode is production --> <context-param> <param-name>tapestry.development-modules</param-name> <param-value> com.example.tutorial.services.DevelopmentModule </param-value> </context-param> <context-param> <param-name>tapestry.qa-modules</param-name> <param-value> com.example.tutorial.services.QaModule </param-value> </context-param> <filter> <filter-name>app</filter-name> <filter-class>org.apache.tapestry5.TapestryFilter</filter-class> </filter> <filter-mapping> <filter-name>app</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
这是简短而熟悉的:你能看到刚才你提供的包名作为tapestry的app包上下文参数显示,tapestry过滤器实例将使用这个信息定位java页类和组件类。
tapestry5作为一个servlet过滤器,而不是作为一个传统的servlet。在这方面,tapestry有机会拦截所有的传入的请求,以确定那些适用与tapestry页(或其他资源)。最终的效果是,你不必写额外的配置,不管你有多少添加到项目中的页和组件。
web.xml文件中的大部分配置是针对tapestry的执行模式的模块类,一个运行模式定义了应用程序如何运行:缺省的执行模式是production(生产环境),但是web.xml定义了两个额外的模式"development"(开发环境) and "qa" (qa就是质量保证).模块类暗示将根据不同的模式被加载,也可以被配置所改变。我们将会回来讲解执行模式和模块类。
tapestry页的最小单位包括一个普通的java类和一个组件模板文件。
在你的web根目录下,一个名为index的页将被所有的请求使用,在上下文名称后没有额外的路径。
Index Java类
tapestry有很特别的规则为所有的页面类,tapestry添加了一个子包pages,附加在根包(com.example.tutorial)之后,java页类也在那里。因此,完整的Java类名是com.example.tutorial.pages.Index
src/main/java/com/example/tutorial/pages/Index.java
package com.example.tutorial.pages; import java.util.Date; import org.apache.tapestry5.annotations.*; import org.apache.tapestry5.ioc.annotations.*; import org.apache.tapestry5.corelib.components.*; import org.apache.tapestry5.SymbolConstants; import org.apache.tapestry5.alerts.AlertManager; /** * Start page of application tutorial1. */ public class Index { @Property @Inject @Symbol(SymbolConstants.TAPESTRY_VERSION) private String tapestryVersion; @InjectComponent private Zone zone; @Persist @Property private int clickCount; @Inject private AlertManager alertManager; public Date getCurrentTime() { return new Date(); } void onActionFromIncrement() { alertManager.info("Increment clicked"); clickCount++; } Object onActionFromIncrementAjax() { clickCount++; alertManager.info("Increment (via Ajax) clicked"); return zone; } }
有一个列表中的index页试图证明一堆不同的想法。即便如此,类本质上是非常简单的。tapestry页和组件没有父类要继承,没有接口需要实现,仅仅是一个简单的pojo(Plain Old Java Object),类的字段和方法有一些特殊的命名约定和注解
你必须满足tapestry的要求。
正如我们看到的那样,运行程序时,程序显示当前的日期和时间,以及一对额外的链接。当前时间的值来自哪里,不久我们将看到值如何在模板引用该值,可以从页面类提取然后输出。
tapestry总是匹配一个页面类到一个模板,不仅如此,事实上,一个页面内的组件总是按相同的方式处理(组件并不总是有模板)。
你会经常听到MVC模型试图控制器模式。在tapestry中,页类实际上既是模型又是控制器(响应用户交互),模板是mvc中的视图,作为一个模型,页公开了javaBean的属性,使得可以在模板中使用。
让我们来看看如何在组件模板的基础上提供完整的用户界面的Java 类
组件模板
tapestry页是一个pojo java类和一个组件模板的组合。模板和java类有同样的名字,但是模板有文件扩展名.tml。这里是com.example.tutorial.pages.Index,模板文件将位于src/main/resource/com/example/tutorial/pages/Index.tml。最终,java类和组件模板将存储于同样的文件夹被部署在war包中。
tapestry组件模板是良好格式的xml文档。这意味这你可以使用任何的xml编辑器。模板甚至有一个DOCTYPE 或者一个XML schema来校验模板页的结构。
大多数情况下,一个tapestry组件模板看起来象一个普通的xhtml。
src/main/resources/com/example/tutorial/pages/Index.tml
<html t:type="layout" title="tutorial1 Index" t:sidebarTitle="Framework Version" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter"> <!-- Most of the page content, including <head>, <body>, etc. tags, comes from Layout.tml --> <p>${message:greeting}</p> <p>The current time is: <strong>${currentTime}</strong></p> <!-- A Zone is a component that can be updated in place, triggered by other components. --> <t:zone t:id="zone"> <p> You have clicked the link <strong>${clickCount}</strong> times. </p> <p> [ <t:actionlink t:id="increment">increment</t:actionlink> ] [ <t:actionlink t:id="incrementAjax" zone="^">increment (via Ajax)</t:actionlink> ] </p> </t:zone> <p:sidebar> <p> You are running Tapestry version <strong>${tapestryVersion}</strong>. </p> <p> [ <t:pagelink page="Index">refresh page</t:pagelink> ] </p> </p:sidebar> </html>
你要命名你的组件模板文件,Index.tml,相同情况下, 作为组件类的名称Index。如果你发现格式错误,有可能是操作系统不同(例如windows开发,部署于linux),这可真麻烦。所以请确保大小写正确。
tapestry的目标是为组件模板,如Index.tml,使其看起来尽可能简单的象静态的html文件,事实上,我们希望在通常的开发中模板就是从html文件开始的,然后再逐渐演化。
tapestry隐藏了所有非标准的元素和属性在xml命名空间内。按惯例,前缀"t:"被用于主要的命名空间,但是这不是必须的,任何你喜欢的前缀都可以。
这短短的模板演示了tapestry的不少功能。
该项目演示了tapestry的不少与众不同的特点包括方法和设计模式,你可不要吃惊噢。
首先,有两个xml的命名空间被定义
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter"
第一个命名空间“t:”用于标识tapestry特殊的元素和属性,虽然是一个XSD (that is, a XML schema definition),它是不完整的(稍后解释)
第2个命名空间"p:",是一个在模板中标记一个区块的方法:通过参数传递到另一个组件,我们不久将扩展这个区块。
tapestry组件模板包含了大多数标准的xhtml,并不变的传递到浏览器。模板的动态方面由组件和扩展表示。
在模板中扩展
让我们开始扩展。扩展包括一些很容易的方法:一些动态的输出当渲染页面时,缺省的,扩展指一个包括javaBean属性的页面。
<p>The current time is: ${currentTime}</p>
如果你使用过tapestry4,则扩展就是一种简洁的替换
大括号内的值是一个属性表达式。tapestry使用自己的属性表达式语言,快速,安全。更高级的属性表达式可以遍历多个属性(例如user.address.city),甚至调用公共方法。这里只是简单的读取当前时间的属性。
tapestry遵循sun公司的javeBean规则,一个属性名currentTime对应两个方法getCurrentTime() and setCurrentTime().如果你忽略了一个方法,那么就变成只写或只读(仅用可用的那个方法)
tapestry做的更好,当匹配属性时它忽略了大小写,在模板中我们可以写成${currenttime} 或 ${CurrentTime}的任何变化,框架会知道如何正确调用。
请注意:tapestry没有必要使得对象持有currentTime的属性,一个模板和一个页总是这样组合:表达式总是源自页面类的实例,这里,是一个Index类的实例。
Index.tml模板包含了第2个表达式
<p>${message:greeting}</p>
在这里,问候语不是页面的属性,它实际上一个本地化的消息键,每个tapestry页和组件允许有其自己的消息目录。
src/main/resources/com/example/tutorial/pages/Index.properties
greeting=Welcome to Tapestry 5! We hope that this project template will get you going in style.
消息目录用于存储重复的模板中的字符串。但他们的主要目的是应用程序本地化,消息可以被跨多页的显示,也可以存储于全局的消息目录里,位于src/main/webapp/WEB-INF/app.properties
这种“message:”前缀并不是特殊格式,有很多内置的前缀,每个都有特定用途,实际上,省略了前缀的表达式等同于使用“prop:”前缀,这意味着其实受到同样的对待。
对于提取信息渲染到客户端,扩展是有用的。但真正复杂的在组件内部。
模板内的组件
组组件模板中,组件可以被表现在两个方面:
1、作为一个普通元素,但有t:type属性被定义
2、作为一个tapestry命令空间,这时,该元素的名称决定组件类型
这里我们使用一个<html>元素来表示应用程序的布局组件。
<html t:type="layout" ...> ... </html>
但是对于页面链接组件PageLink,我们使用了一个命名空间的元素
<t:pagelink page="Index">refresh page</t:pagelink>
至于哪一种形式只是个选择的问题,大多数情况下他们等价。
至于其他地方,格式将被忽略,这里的类型(layout和pagelink)在这里小写,实际的类名是Layout和PageLink。此外,tapestry融合了核心库组件在组件中,从而类型layout映射了组件类com.example.tutorial.components.Layout,但是pagelink映射的是框架内置的org.apache.tapestry5.corelib.components.PageLink
tapestry组件被配置成可以使用参数,每个组件有一些参数,每个都有特殊的目的。一些参数是必须的,一些参数可选。元素属性被用于绑定参数字符串,或者页面属性,tapestry是灵活的,你可以总是放置属性在tapestry的命名空间(使用t:前缀)但是多数情况下,这是不必要的。
<html t:type="layout" title="tutorial1 Index" t:sidebarTitle="Framework Version" ...
结合两个参数,title和sidebarTitle,在布局组件中使用字符串"tutorial1 index"和"current Time"
布局组件将实际提供大量的html发送给浏览器。问题是,页面的模板集中到布局组件的模板。下图显示了如何将参数传递给最终呈现在最后一页上的Layout组件。
有趣的地方是(这是一种先进理念,稍后将提及),我们可以把一大块的Index.tml模板作为边栏参数传递给布局组件,这就是tapestry,参数命名空间"p:"前缀,元素名字匹配了组件的参数并且传递给整个布局组件,模板内该块被渲染。
<t:pagelink page="Index">refresh</t:pagelink>
这一次,PageLink组件的页面参数被绑定到字符串“Index”(这是页面的名称)。这被呈现为作为URL得到渲染,这使得currentTime得到更新。你也可以创建链接到别的页面,将来我们还能看到,不仅仅是页面名称,也可以附加别的信息到URL。
一个魔术
这是一个魔术。修改 Index.java并且改变 getCurrentTime() 方法:
Index.java (partial)
public String getCurrentTime() { return "A great day to learn Tapestry"; }
保存文件,然后刷新浏览器。
这是一个tapestry的java要素特征,立刻改变你 的组件类(这是一个功能,我们称之为存活类重加载),No restrart,No re-deploy(无需重启动,无需重新部署),你的工作不会变慢。
但是。。。如果你的程序有错怎么办?如果你得到错误的模板的名称。尝试一下,在模板中改变${currentTime},看看结果会怎样?
这是tapestry的异常报告页面。相当详细。这里清楚的显示了tapestry做了些什么,定位到了模板的行数。tapestry总是扩展自整个异常栈,实际上,如果我们向下滚动一点,能看到更多细节,和一点帮助。
这是部分的tapestry的方法:不仅表明了他到底是什么在做什么,哪里出了问题,它甚至可以帮助你找到解决办法,这里它会告诉你可以使用哪些属性名字。
tapestry显示了整个异常栈,和大量的环境细节:包括HttpSession(如果有),JVM,向下滚动可以看到所有的信息。
注:当前的情况表明应用程序配置在development(开发模式),而不是生产模式。无论如何,大多数生产环境的应用程序会更进一步的自己定制如何报告错误和异常信息。
本页面有很多链接和ajax的东西,我们暂时忽略。
上一篇: