浏览项目文件

项目结构遵循Maven的标准:


  • Java 源文件在 src/main/java 下
  • Web应用文件在 src/main/webapp (包含 src/main/webapp/WEB-INF)下
  • Java 测试文件在 src/test/java 下
  • 非编码文件 (包含 Tapestry 页面 and 组件 模板) 在 src/main/resources and src/test/resources 下

打开web.xml配置文件,看看Maven为我们做了什么:

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.tutorial1</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.tutorial1.services.DevelopmentModule
        </param-value>
    </context-param>
    <context-param>
        <param-name>tapestry.qa-modules</param-name>
        <param-value>
            com.example.tutorial1.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-package的上下文参数是你早些提供的; TapestryFilter 实例会使用这些信息来指定Java类和组件的位置。

Tapestry 操作是Servlet 过滤器而不是传统的Servlet. 在这种模式下, Tapestry 才能拦截所有的请求,来决定展现哪一个Tapestry 页面(或其它资源).在网络环境下,你不需要配置Tapestry的操作 ,尽管你添加了N多的页面或组件到你的应所有。

web.xml 的其余配置是用来匹配模式类的Tapestry 执行模式,执行模式决定了应用如何运行:默认为 'production', 但是web.xml 定义了其它两种模式:'development' 和 'qa' ("Quality Assurance"的简写) 。模式类会隐式加载这些执行模式,当然你也可以改变这些配置。我们将会在稍后讨论这个问题。

Tapestry 页面 仅仅是由Java 类加上一个组件模板组成。

在你的web应用的根目录,Index页面将会使用任意不需要添加上下文目录的请求。

Index Java 类

Tapestry 对于放置Java 类的位置拥有非常明确的规则。 Tapestry 在应用根包里(com.xxxx.tutorial1)添加了一个子包 "pages",Java 类都在这里放置。因此,Java 类的全名叫 com.xxxx.tutorial1.pages.Index.

src/main/java/com/example/tutorial/pages/Index.java
package com.example.tutorial1.pages;

import org.apache.tapestry5.Block;
import org.apache.tapestry5.EventContext;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.annotations.InjectPage;
import org.apache.tapestry5.annotations.Log;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.services.HttpError;
import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
import org.slf4j.Logger;

import java.util.Date;

/**
 * Start page of application tutorial1.
 */
public class Index
{
    @Inject
    private Logger logger;

    @Inject
    private AjaxResponseRenderer ajaxResponseRenderer;

    @Property
    @Inject
    @Symbol(SymbolConstants.TAPESTRY_VERSION)
    private String tapestryVersion;

    @InjectPage
    private About about;

    @Inject
    private Block block;

    // Handle call with an unwanted context
    Object onActivate(EventContext eventContext)
    {
        return eventContext.getCount() > 0 ? new HttpError(404, "Resource not found") : null;
    }

    Object onActionFromLearnMore()
    {
        about.setLearn("LearnMore");

        return about;
    }

    @Log
    void onComplete()
    {
        logger.info("Complete call on Index page");
    }

    @Log
    void onAjax()
    {
        logger.info("Ajax call on Index page");

        ajaxResponseRenderer.addRender("middlezone", block);
    }

    public Date getCurrentTime()
    {
        return new Date();
    }
}

由于Index页尝试解释一系列不同的Tapestry 用法,使得这个类有点长。尽管如此,这个类还是很简单:没有继承基类,没有实现接口,只是一个有了属性和方法的纯POJO。

你肯定对Tapestry有了大致的了解:

  • 必须把Java 类放在指定包位置,这里是 com.example.tutorial1.pages
  • 类必须是 public
  • 确保有一个 public 并且无参的构造器 (本例中,Java 编译器已经隐式为我们提供了--个人观点:继承了Object)
  • 所有非静态属性必须是 private

当运行应用式,我们看到页面显示了当前日期和时间,以及其它一些链接。当前时间就是从Java类里属性来的,(稍后我们来讲一下这个值是怎么应用到模板的)因此,属性可以在页面和类中交互.

Tapestry 中,一个页面类匹配一个模板,实际上,页面内组件也是这样(除非这个组件没有模板引用).

你应该经常听到 模型-视图-控制器(MVC) 模式. 在 Tapestry 里,页面类扮演着模型 (数据源)和控制器(用户的操作和逻辑响应).页面模板就是MVC里的视图 。作为一个模型,页面类开放java属性给页面引用。

让我们看看页面组件模板在Java类提供的用户交互里是如何构建的。

组件模板

Tapestry 页面由POJO Java 类和Tapestry 组件模板组成。模板拥有和Java类一样的名字,但后缀是.tml. 所以 这里的Java 类叫做 com.example.tutorial.pages.Index, 模板文件将会也必须出现在 src/main/resource/com/example/tutorial/pages/Index.tml. 最后,经过打包部署WAR文件后,Java类和模板文件都会存在同一个文件夹下。

Tapestry 组件模板遵循XML文件规范. 也就是说你可以使用任意的xml编辑器。 模板可以拥有一个 DOCTYPE 或 XML schema(大纲?) 来规范当前内容.

注意:Tapestry 解析组件模板时使用的是不通过式解析,它会校验合法的格式性诸如标点,节点平衡,属性有""等,所以需要你来把这些基本的错误解决,Tapestry才能编译通过。

大多数来说,一个Tapestry 组件模板和XHTML差不多。

src/main/resources/com/example/tutorial1/pages/Index.tml
<html t:type="layout" title="tutorial1 Index"
      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd"
      xmlns:p="tapestry:parameter">

    <div class="hero-unit">
        <p>
            <img src="${asset:context:images/tapestry.png}"
                 alt="${message:greeting}" title="${message:greeting}"/>
        </p>
        <h3>${message:greeting}</h3>
        <p>The current time is: <strong>${currentTime}</strong></p>
        <p>
            This is a template for a simple marketing or informational website. It includes a large callout called
            the hero unit and three supporting pieces of content. Use it as a starting point to create something
            more unique.
        </p>
        <p><t:actionlink t:id="learnmore" class="btn btn-primary btn-large">Learn more &raquo;</t:actionlink></p>
    </div>

    <div class="row">
        <div class="span4">
            <h2>Normal link</h2>
            <p>Clink the bottom link and the page refresh with event <code>complete</code></p>
            <p><t:eventlink event="complete" class="btn btn-default">Complete&raquo;</t:eventlink></p>
        </div>
        <t:zone t:id="middlezone" class="span4">

        </t:zone>
        <div class="span4">
            <h2>Ajax link</h2>
            <p>Click the bottom link to update just the middle column with Ajax call with event <code>ajax</code></p>
            <p><t:eventlink event="ajax" zone="middlezone" class="btn btn-default">Ajax&raquo;</t:eventlink></p>
        </div>
    </div>

    <t:block t:id="block">
        <h2>Ajax updated</h2>
        <p>I'v been updated through AJAX call</p>
        <p>The current time is: <strong>${currentTime}</strong></p>
    </t:block>

</html>

你必须为你的模板文件命名为 Index.tml, 而且必须和类名的大小写一致。如果写错了,也行会在一部分系统中运行正常(比如Mac Windows),但是在其它系统却不能运行(比如Linux和大部分其它系统)。由于普通情况下我们都是在Windows开发,发布在Linux或者Solaris,这样的错误会令我们抓狂。因此,小心对待这个问题。

Tapestry 的模板如 Index.tml 的目标是看起来和普通静态页面一样(静态是只不变的意思,对应动态的Tapestry 页面)

实际上, 许多情况下我们期望模板类似静态HTML文件开头,由美工开发,然后用作Tapestry 动态页面。

Tapestry 隐藏了XML里非标准元素以及属性。为了方便使用,t: 被用于命名前缀,但这不是必须的,你可以指定任意你想使用的前缀。

此模板解释了一些Tapestry的特性。

快速开始部分介绍了许多不同的特性,方法,普通模式在Tapestry中的使用,我们来看一下:

首先,有两个普通的XML命名空间定义:

  xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd"
  xmlns:p="tapestry:parameter"

第一个 "t:", 用于标记Tapestry 的特殊元素和属性。虽然以经有了XSD(一种XML语法定义),但是不方便使用(稍后我们来说一下原因)

第二个 "p:", 引入其它模板块的一种方式,稍后我们将会展开说明

一个Tapestry 组件模板由大部分的标准XHTML组成,大部分会直接传递到客户浏览器,模板的动态部分由组件和扩展来完成。

Templates里的扩展

让我们从扩展开始. 当动态渲染页面时,扩展是输出的一种简单方式,默认情况下,一个扩展属性指向当前页面java类的属性。

  <p>The current time is: ${currentTime}</p>

大括号里面是属性表达式。 Tapestry 使用自己直观,快捷,类型安全的属性表达式。

Tapestry 不会使用反射来实现属性表达式

更多高级属性表达式可以使用点属性 (比如 user.address.city), 甚至可以调用方法.这里的扩展仅仅读取了当前页面的currentTime属性  .

Tapestry 遵循Sun公司置顶的JavaBean规范: currentTime属性映射到两个方法getCurrentTime()和 setCurrentTime(). 如果省略不写,代表你不需要使用。这里就只有getCurrentTime()方法,所以 currentTime 是只读的(或者只写另外一种setter,只写属性). (要对JavaBean属性保持清醒,方法名称起着重要作用,实例变量是否存在不是必要的)

Tapestry 可以走的更远: 当为页面匹配内在扩展时,它是不区分大小写的。在模板中,无论你写成 ${currenttime} 或 ${CurrentTime}亦或其它大小写情况,Tapestry 依然会调用getCurrentTime() 方法.

 Tapestry中没有必要说明 哪个对象拥有 currentTime 属性; 模板和页面通常互相结合使用;表达式通常在页面实例中,本例中是Index 类的实例.

 Index.tml 模板包含了另一中表达式:

    <p>${message:greeting}</p>

这里 greeting 不是页面的属性,它实际上是一个本地化的Key值。每一个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:" 前缀没有什么特殊,只是一些Tapestry中的具有特殊意义的固有前缀。实际上,在扩展中省略前缀和使用固有"prop:"前缀一样,可以看成属性表达式。

扩展在抽取信息和渲染到客户端页面方面十分有用,但它在组件中更加有用。

模板中的组件

组件在组件模板中的作用体现在两个方面 :

  • 作为普通元素,但是以 t:type 属性来定义说明这是组件类型.
  • 作为Tapestry命名空间元素,决定了元素的类型.

这里我们使用 <html>元素来代表应用布局组件.

<html t:type="layout" ...> 
  ...
</html>

但是 EventLink 组件, 我们使用 Tapestry 命名空间元素:

<t:eventlink page="Index">refresh page</t:eventlink>

选择使用哪一种由你来决定。大多数情况下,他们是相同的。

上面我们说大小写是不敏感的,这里使用的类型(layout和eventlink)都是小写的;实际上类名都是大写的:Layout和EventLink。在核心库里,Tapestry混合了应用组件定义,因此, "layout"类型映射到了组件类com.example.tutorial.components.Layout, 但 "event link"映射到了 Tapestry's 构建类 org.apache.tapestry5.corelib.components.EventLink.

Tapestry 组件通常作为参数来配置,对于每个组件来说,都有一系列自己的参数,和本身特殊的类型和作用。有些参数是必须的,有些是可选的。元素属性通常用户绑定特殊文本值或是页面属性。Tapestry 也很灵活:你可以一直使用Tapestry的命名空间( "t:" 前缀),但不是必须的。

<html t:type="layout" title="tutorial1 Index"
      p:sidebarTitle="Framework Version" ...

这里绑定了两个参数,title 和 sidebarTitle,  代表了Layout 组件的名字 "tutorial1 Index" 和 "Framework Version".

布局组件实际上提供了向浏览器发送的空白HTML,我们在稍后章节讲解。页面模板嵌入了组件布局模板,下表为你说明参数如何传递到布局模板,然后渲染到最终页面

Templates and Parameters

这里有趣的是(Tapestry里的高级概念,稍后讲解 )我们可以使用布局组件作为参数放入Index.tml模板里,这就是tapestry:parameter的命名空间 (p:前缀),元素的名称匹配到组件参数,然后整个模板块传到布局组件里...这就决定了内在模板块也被渲染。

<t:eventlink event="complete" class="btn btn-default">Complete&raquo;</t:eventlink>

此时,页面组件PageLink被绑定到Index页面,这里将会作为URL输出到页面链接,所以当前时间才会更新。你也可以创建到应用其它页面的链接,我们在稍后的章节讲解。

魔法效应

是时候看点魔法的东西了,编辑Index.java 修改getCurrentTime() 为 :

Index.java (partial)
  public String getCurrentTime()
  {
    return "A great day to learn Tapestry";
  }

确保你保存文件后,点击浏览器的刷新按钮:

这是Tapestry一个早期令人惊奇的特性:修改你的组件模板,效果即使生效。  (称之为 Live Class Reloading 特性). 不需要重新启动,不需要重新部署。改变代码,立即查看。没有什么会减缓你的速度或增加完成工作时间。

但是 ... 如果你犯了错会怎样?如果你在模板里写错了名字?试一下;在模板里,修改 ${currentTime} 为 ${currenTime}, 看一下你会得到什么

这里是Tapestry 异常报告页面,非常的详细。它明确展示了Tapestry要做什么,遇到了模板中直接定位到上下文的问题,Tapestry 通常展示了整个栈的异常信息,因为异常一般都是被抛出,捕获,或者被其它异常再度抛出。实际上,如果我们往下滚动一点,我们会看到该异常的详细信息,外带一点提示:

这就是Tapestry的风格: 不仅指出在做什么时遇到了什么问题,还会提示你解决方案。这里提示你可用的属性名称。

信息的详细程度可以在应用里配置:以开发模式运行应用而不是上线模式。在上线模式中,异常报告只会输出最顶级的异常信息.实际上,大部分的上线应用都会处理好异常的。

Tapestry 展示了更深的栈异常信息,并伴随着应用运行环境的异常:当前请求,Session(如果存在)甚至JVM的详细信息。继续滚动可以查看。

接下来: 实现HI-LO猜游戏

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值