Contents

Apache Tapestry 简介

1.概述

如今,从社交网络到银行业务,从医疗保健到政府服务,所有活动都可以在线进行。因此,他们严重依赖 Web 应用程序。

Web 应用程序使用户能够消费/享受公司提供的在线服务。同时,它充当后端软件的接口。

在这个介绍性教程中,我们将探索 Apache Tapestry Web 框架并使用它提供的基本功能创建一个简单的 Web 应用程序。

2. Apache Tapestry

Apache Tapestry 是一个基于组件的框架,用于构建可扩展的 Web 应用程序。

它遵循约定优于配置的范式,并使用注释和命名约定进行配置。

所有组件都是简单的 POJO。同时,它们是从零开始开发的,不依赖于其他库。

除了 Ajax 支持之外,Tapestry 还具有出色的异常报告功能。它还提供了一个广泛的内置通用组件库。

在其他重要功能中,一个突出的特点是代码的热重载。因此,使用此功能,我们可以立即看到开发环境中的变化。

3. 设置

Apache Tapestry 需要一组简单的工具来创建 Web 应用程序:

  • Java 1.6 或更高版本
  • 构建工具(Maven 或 Gradle)
  • IDE(Eclipse 或 IntelliJ)
  • 应用服务器(Tomcat 或 Jetty)

在本教程中,我们将结合使用 Java 8、Maven、Eclipse 和 Jetty Server。

要设置最新 的Apache Tapestry 项目,我们将使用Maven 原型 并按照官方文档提供的说明 进行操作:

$ mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org

或者,如果我们有一个现有项目,我们可以简单地将Tapestry-core Maven 依赖项添加到pom.xml

<dependency>
    <groupId>org.apache.tapestry</groupId>
    <artifactId>tapestry-core</artifactId>
    <version>5.4.5</version>
</dependency>

一旦我们准备好设置,我们可以通过以下 Maven 命令启动应用程序apache-tapestry

$ mvn jetty:run

默认情况下,可以通过localhost:8080/apache-tapestry 访问该应用程序:

/uploads/apache_tapestry/1.png

4. 项目结构

让我们探索一下 Apache Tapestry 创建的项目布局:

/uploads/apache_tapestry/3.png

我们可以看到一个类似 Maven 的项目结构,以及一些基于约定的包。

Java 类被放置在src/main/java并被分类为componentspagesservices

同样,src/main/resources包含我们的模板(类似于 HTML 文件)——它们具有*.tml*扩展名。

对于放置在componentspages目录下的每个 Java 类,都应该创建一个同名的模板文件。

src/main/webapp目录包含图像、样式表和 JavaScript 文件等资源。同样,测试文件放在src/test中。

最后,src/site将包含文档文件。

为了更好的理解,我们来看看在 Eclipse IDE 中打开的项目结构:

/uploads/apache_tapestry/5.png

5. 注释

让我们讨论Apache Tapestry 为日常使用提供的一些方便的注释 。展望未来,我们将在我们的实现中使用这些注释。

5.1.@Inject

@Inject 注解在org.apache.tapestry5.ioc.annotations包中可用,它提供了一种在 Java 类中注入依赖项的简单方法。

这个注解对于注入资产、块、资源和服务非常方便。

5.2. @InjectPage

org.apache.tapestry5.annotations包中可用,@InjectPage 注释允许我们将页面注入另一个组件。此外,注入的页面始终是只读属性。

5.3. @InjectComponent

类似地,@InjectComponent 注解允许我们注入模板中定义的组件。

5.4. @Log

@Log 注释在org.apache.tapestry5.annotations包中可用,并且可以方便地在任何方法上启用 DEBUG 级别的日志记录。它记录方法的进入和退出,以及参数值。

5.5. @Property

org.apache.tapestry5.annotations包中可用, @Property 注释将字段标记为属性。同时,它会自动为属性创建 getter 和 setter。

5.6. @Parameter

类似地,*@Parameter *注解表示一个字段是一个组件参数。

6. 页面

因此,我们都准备好探索框架的基本功能。让我们在我们的应用程序中创建一个新的Home

首先,我们将在src/main/javapages目录中定义一个 Java 类Home

public class Home {
}

6.1.模板

然后,我们将在src/main/resources下的pages目录中创建一个对应的Home.tml模板

扩展名为*.tml*(Tapestry 标记语言)的文件类似于 Apache Tapestry 提供的具有 XML 标记的 HTML/XHTML 文件。

例如,让我们看一下Home.tml模板:

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
    <head>
        <title>apache-tapestry Home</title>
    </head>
    <body>
        <h1>Home</h1>
    </body>
</html>

瞧!只需重启 Jetty 服务器,我们就可以在localhost:8080/apache-tapestry/home访问主页:

/uploads/apache_tapestry/7.png

6.2. 属性

让我们探索如何在Home上呈现属性。

为此,我们将在Home类中添加一个属性和一个 getter 方法:

@Property
private String appName = "apache-tapestry";
public Date getCurrentTime() {
    return new Date();
}

要在主页上呈现appName属性,我们可以简单地使用*${appName}*。

同样,我们可以编写*${currentTime}从页面访问getCurrentTime*方法。

6.3. 本土化

Apache Tapestry 提供集成的本地化 支持。按照惯例,页面名称属性文件保存要在页面上呈现的所有本地消息的列表。

例如,我们将在主页的pages目录中创建一个带有本地消息的home.properties文件:

introMsg=Welcome to the Apache Tapestry Tutorial

消息属性不同于 Java 属性。

出于同样的原因,带有消息前缀的键名用于呈现消息属性——例如,${message:introMsg}

6.4. 布局组件

让我们通过创建Layout.java类来定义一个基本的布局组件。我们将文件保存在src/main/javacomponents目录中:

public class Layout {
    @Property
    @Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
    private String title;
}

在这里,title属性被标记为必需,并且绑定的默认前缀设置为文字String

然后,我们会在src/main/resourcescomponents目录下编写一个对应的模板文件Layout.tml

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
    <head>
        <title>${title}</title>
    </head>
    <body>
        <div class="container">
            <t:body />
            <hr/>
            <footer>
                <p>© Your Company</p>
            </footer>
        </div>
    </body>
</html>

现在,让我们使用主页上的layout

<html t:type="layout" title="apache-tapestry Home" 
    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
    <h1>Home! ${appName}</h1>
    <h2>${message:introMsg}</h2>
    <h3>${currentTime}</h3>
</html>

请注意,命名空间 用于标识Apache Tapestry 提供的元素( t:typet:body )。同时,命名空间还提供了组件和属性。

在这里,t:type将设置主页上的layout。并且,t:body元素将插入页面的内容。

让我们看一下带有布局的主页:

/uploads/apache_tapestry/9.png

7. 表格

让我们创建一个带有表单的登录页面,以允许用户登录。

如前所述,我们将首先创建一个 Java 类Login

public class Login {
    // ...
    @InjectComponent
    private Form login;
    @Property
    private String email;
    @Property
    private String password;
}

在这里,我们定义了两个属性—— emailpassword。此外,我们还为登录注入了一个Form组件。

然后,让我们创建一个对应的模板login.tml

<html t:type="layout" title="apache-tapestry com.example"
      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
      xmlns:p="tapestry:parameter">
    <t:form t:id="login">
        <h2>Please sign in</h2>
        <t:textfield t:id="email" placeholder="Email address"/>
        <t:passwordfield t:id="password" placeholder="Password"/>
        <t:submit class="btn btn-large btn-primary" value="Sign in"/>
    </t:form>
</html>

现在,我们可以在localhost:8080/apache-tapestry/login 访问登录页面:

/uploads/apache_tapestry/11.png

8. 验证

Apache Tapestry 提供了一些用于表单验证 的内置方法。它还提供了处理表单提交成功或失败的方法。

内置方法遵循事件和组件名称的约定。例如,方法onValidationFromLogin将验证Login组件。

同样,像onSuccessFromLoginonFailureFromLogin这样的方法分别用于成功和失败事件。

因此,让我们将这些内置方法添加到Login类中:

public class Login {
    // ...
    
    void onValidateFromLogin() {
        if (email == null)
            System.out.println("Email is null);
        if (password == null)
            System.out.println("Password is null);
    }
    Object onSuccessFromLogin() {
        System.out.println("Welcome! Login Successful");
        return Home.class;
    }
    void onFailureFromLogin() {
        System.out.println("Please try again with correct credentials");
    }
}

9. 警报

如果没有适当的警报,表单验证是不完整的。更不用说,该框架还内置了对警报消息的支持。

为此,我们将首先在Login类中注入AlertManager 的实例来管理警报。然后,将现有方法中的println语句替换为警告消息:

public class Login {
    // ...
    @Inject
    private AlertManager alertManager;
    void onValidateFromLogin() {
        if(email == null || password == null) {
            alertManager.error("Email/Password is null");
            login.recordError("Validation failed"); //submission failure on the form
        }
    }
 
    Object onSuccessFromLogin() {
        alertManager.success("Welcome! Login Successful");
        return Home.class;
    }
    void onFailureFromLogin() {
        alertManager.error("Please try again with correct credentials");
    }
}

让我们看看登录失败时的警报:

/uploads/apache_tapestry/13.png

10. Ajax

到目前为止,我们已经探索了使用表单创建一个简单的主页。同时,我们看到了对警报消息的验证和支持。

接下来,让我们探索一下 Apache Tapestry 对 Ajax 的内置支持。

首先,我们将在Home类中注入AjaxResponseRendererBlock 组件的实例。然后,我们将创建一个onCallAjax方法来处理 Ajax 调用:

public class Home {
    // ....
    @Inject
    private AjaxResponseRenderer ajaxResponseRenderer;
    @Inject
    private Block ajaxBlock;
    @Log
    void onCallAjax() {
        ajaxResponseRenderer.addRender("ajaxZone", ajaxBlock);
    }
}

此外,我们需要在Home.tml中进行一些更改。

首先,我们将添加eventLink 以调用onCallAjax方法。然后,我们将添加一个id 为ajaxZone的zone 元素来呈现 Ajax 响应。

最后,我们需要一个块组件,该组件将被注入Home类并呈现为 Ajax 响应:

<p><t:eventlink event="callAjax" zone="ajaxZone" class="btn btn-default">Call Ajax</t:eventlink></p>
<t:zone t:id="ajaxZone"></t:zone>
<t:block t:id="ajaxBlock">
    <hr/>
    <h2>Rendered through Ajax</h2>
    <p>The current time is: <strong>${currentTime}</strong></p>
</t:block>

我们来看看更新后的主页:

/uploads/apache_tapestry/15.png

然后,我们可以单击 Call Ajax 按钮并查看ajaxResponseRenderer的运行情况:

/uploads/apache_tapestry/17.png

11. 日志

要启用内置的日志记录功能,需要注入Logger 的实例。然后,我们可以使用它来记录任何级别的日志,例如 TRACE、DEBUG 和 INFO。

因此,让我们在Home类中进行必要的更改:

public class Home {
    // ...
    @Inject
    private Logger logger;
    void onCallAjax() {
        logger.info("Ajax call");
        ajaxResponseRenderer.addRender("ajaxZone", ajaxBlock);
    }
}

现在,当我们单击 Call Ajax 按钮时,Logger将在 INFO 级别记录:

[INFO] pages.Home Ajax call