Apache Tapestry - 快速指南


Apache Tapestry - 概述

Apache Tapestry 是一个用 Java 编写的开源 Web 框架。它是一个基于组件的网络框架。Tapestry 组件是 Java 类。它们既不是从框架特定的基类继承,也不是接口的实现,它们只是普通的 POJO(普通旧 Java 对象)。

Tapestry使用的Java的重要特性是Annotation。Tapestry 网页是使用一个或多个组件构建的,每个组件都有一个基于 XML 的模板和用大量 Tapestry 注释装饰的组件类。Tapestry 可以创建任何内容,从小型的单页 Web 应用程序到包含数百个页面的大型应用程序。

挂毯的好处

挂毯提供的一些好处是 -

  • 高度可扩展的 Web 应用程序。
  • 自适应 API。
  • 快速且成熟的框架。
  • 持久状态存储管理。
  • 内置控制反转。

挂毯的特点

Tapestry 具有以下特点 -

  • 直播课重装
  • 清晰详细的异常报告
  • 静态结构,动态Behave。
  • 广泛使用普通旧 Java 对象 (POJO)
  • 代码更少,交付更多。

为什么是挂毯?

Java已经有很多Web框架,如JSP、Struts等,那么,为什么我们还需要另一个框架呢?当今大多数 Java Web 框架都很复杂并且学习曲线陡峭。它们是老式的,每次更新都需要编译、测试和部署周期。

另一方面,Tapestry 通过提供实时类重新加载,提供了一种现代的 Web 应用程序编程方法。虽然其他框架引入了大量接口、抽象和基类,但 Tapestry 仅引入了一小部分注释,并且仍然提供了编写具有丰富 AJAX 支持的大型应用程序的能力。

Apache Tapestry - 架构

Tapestry 尝试尽可能多地使用 Java 的可用功能。例如,所有 Tapestry 页面都是简单的 POJO。它不强制执行任何自定义接口或基类来编写应用程序。相反,它使用注释(扩展 Java 类功能的轻量级选项)来提供功能。它基于久经考验的Java Servlet API,并作为 Servlet Filter 实现。它为 Web 应用程序提供了一个新的维度,并且编程非常简单、灵活、易于理解和健壮。

工作流程

让我们讨论请求挂毯页面时发生的操作顺序。

工作流程

步骤 1 - Java Servlet接收页面请求。该 Java Servlet 的配置方式是将传入请求转发到 Tapestry。配置在web.xml中完成,如以下程序中指定。Filter 和 Filter Mapping 标签将所有请求重定向到Tapestry Filter

<!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>My Tapestry Application</display-name> 
   <context-param> 
      <param-name>tapestry.app-package</param-name> 
      <param-value>org.example.myapp</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> 

步骤 2 - Tapestry 过滤器通过其Service()方法调用HttpServletRequestHandler服务。

步骤 3 - HttpServletRequestHandler将请求和响应存储在RequestGlobals中。它还将请求和响应包装为 Request 和 Response 对象并将其发送到 RequestHandler。

步骤 4 - RequestHandler是Servlet API 的HttpServletRequest之上的抽象。Tapestry 的一些显着功能是在RequestHandler部分完成的。Tapestry的功能可以通过在RequestHandler中编写过滤器来扩展。RequestHandler 提供了几个内置过滤器,其中包括 -

  • CheckForUpdates Filter - 负责实时类重新加载。该过滤器检查 java 类的更改并根据需要更新应用程序。

  • 本地化过滤器- 识别用户的位置并为应用程序提供本地化支持。

  • StaticFiles Filter - 识别静态请求并中止进程。一旦进程中止,Java Servlet 就会接管控制权并处理请求。

  • 错误过滤器- 捕获未捕获的异常并显示异常报告页面。

RequestHandler 还修改并存储 RequestQlobals 中的请求和响应,并调用 MasterDispatcher 服务。

步骤 5 - MasterDispatcher负责通过按照特定顺序调用多个调度程序来呈现页面。MasterDispatcher 调用的四个主要调度程序如下 -

  • RootPath Dispatcher - 它识别请求的根路径“/”并呈现与起始页相同的内容。

  • 资产调度程序- 它通过检查 url 模式 /assets/ 来识别资产(Java 资产)请求,并将请求的资产作为字节流发送。

  • PageRender Dispatcher - 大部分挂毯操作在 PageRender Dispatcher 和下一个调度程序 Component Dispatcher 中完成。该调度程序识别该请求的特定页面及其激活上下文(额外信息)。然后它呈现该特定页面并将其发送到客户端。例如,如果请求 url 为 /product/12123434,则调度程序将检查是否有任何名称为 Product/12123434 的类可用。如果找到,它会调用product/12123434类,生成响应并将其发送到客户端。如果没有,它会检查产品类别。如果找到,它会使用额外信息 121234434 调用产品类,生成响应并将其发送到客户端。这些额外信息称为激活上下文。如果没有找到类,它只是将请求转发给组件调度程序。

  • 组件调度程序- 组件调度程序将页面的 URL 与模式匹配 - /<class_name>/<component_id>:<event_type>/<activation_context>。例如,/product/grid:sort/asc 表示产品类、网格组件、sortevent 类型和 asc 激活上下文。这里,event_type 是可选的,如果没有提供,将触发默认的事件类型操作。通常,组件调度程序的响应是将重定向发送到客户端。大多数情况下,重定向将在下一个请求中与 PageRender Dispatcher 匹配,并且正确的响应将发送到客户端。

Apache Tapestry - 安装

在本章中,我们将讨论如何在我们的计算机上安装 Tapestry。

先决条件

Tapestry 的唯一依赖项是Core Java。Tapestry是独立开发的,没有使用任何第三方库/框架。甚至 Tapestry 使用的 IoC 库也是从头开始开发的。用 Tapestry 编写的 Web 应用程序可以从控制台本身构建和部署。

我们可以使用Maven、EclipseJetty来改善开发体验。Maven 提供快速启动应用程序模板和选项,以在 Jetty(Java 事实上的开发服务器)中托管应用程序。Eclipse 提供了广泛的项目管理功能,并与 Maven 很好地集成。

理想的挂毯应用程序开发需要以下内容 -

  • Java 1.6 或更高版本
  • 阿帕奇Maven
  • Eclipse集成开发环境
  • 码头服务器

验证 Maven 安装

希望您已经在计算机上安装了 Maven。要验证 Maven 安装,请输入以下命令 -

mvn --version

您可以看到如下所示的响应 -

Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-1110T22:11:47+05:30) 
Maven home: /Users/workspace/maven/apache-maven-3.3.9 
Java version: 1.8.0_92, vendor: Oracle Corporation 
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre 
Default locale: en_US, platform encoding: UTF-8 
OS name: "mac os x", version: "10.11.4", arch: "x86_64", family: "mac"

如果尚未安装 Maven,请访问Maven网站下载并安装最新版本的 Maven。

下载挂毯

Tapestry的最新版本是5.4,可以从Tapestry网站下载。下载二进制包就足够了。如果我们使用Maven快速启动模板,那么就没有必要单独下载Tapestry。Maven 会自动下载必要的 Tapestry Jars 并配置应用程序。我们将在下一章讨论如何使用 Maven 创建基本的 Tapestry 应用程序。

Apache Tapestry - 快速入门

安装 Tapestry 后,让我们使用 Maven 创建一个新的初始项目,如下所示 -

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

您可以看到如下所示的响应 -

[INFO] Scanning for projects... 
[INFO] 
[INFO] --------------------------------------------------------------------------------- 
[INFO] Building Maven Stub Project (No POM) 1 
[INFO] ---------------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:2.4:generate (default-cli) > 
generatesources @ standalone-pom >>> 
[INFO]  
[INFO] <<< maven-archetype-plugin:2.4:generate (default-cli) 
< generatesources @ standalone-pom <<< 
[INFO] 
[INFO] --- maven-archetype-plugin:2.4:generate (default-cli) @ standalone-pom --- 
[INFO] Generating project in Interactive mode 
[INFO] No archetype defined. Using maven-archetype-quickstart 
(org.apache.maven.archetypes:maven-archetype-quickstart:1.0)

Maven 构建所有操作后,选择 archetype 创建Tapestry 5 QuickStart项目,如下所示 -

选择原型-

选择一个数字或应用过滤器(格式:[groupId:]artifactId,区分大小写包含): : 1

现在您将得到如下所示的响应 -

Choose org.apache.tapestry:quickstart version: 
1: 5.0.19
2: 5.1.0.5 
3: 5.2.6 
4: 5.3.7 
5: 5.4.1 

提取快速入门版本号如下 -

Choose a number: 5: 5

此处,QuickStart 项目采用选项 5 的版本“5.4.1”。现在,Tapestry 原型会一一询问以下信息,如下所示 -

  • 5.1 groupId - 定义属性“groupId”的值: : com.example

  • 5.2 artifactId - 定义属性“artifactId”的值::Myapp

  • 5.3 版本- 定义属性“版本”的值:1.0-SNAPSHOT::

  • 5.4 包名称- 定义属性“package”的值: com.example: : com.example.Myapp

现在您的屏幕要求您确认 -

确认属性配置 -

  • groupId - com.example

  • artifactId - 我的应用程序

  • 版本- 1.0-SNAPSHOT

  • - com.example.Myapp

使用下面所示的选项验证所有属性并确认更改 -

 Y: : Y 

您将看到如下所示的屏幕。

[INFO] ---------------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: quickstart:5.4.1 
[INFO] ---------------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.example 
[INFO] Parameter: artifactId, Value: Myapp 
[INFO] Parameter: version, Value: 1.0-SNAPSHOT 
[INFO] Parameter: package, Value: com.example.Myapp 
[INFO] Parameter: packageInPathFormat, Value: com/example/Myapp 
[INFO] Parameter: package, Value: com.example.Myapp 
[INFO] Parameter: version, Value: 1.0-SNAPSHOT 
[INFO] Parameter: groupId, Value: com.example 
[INFO] Parameter: artifactId, Value: Myapp 
[WARNING] Don't override file /Users/workspace/tapestry/Myapp/src/test/java 
[WARNING] Don't override file /Users/workspace/tapestry/Myapp/src/main/webapp 
[WARNING] Don't override file /Users/workspace/tapestry/Myapp/src/main/resources/com/
example/Myapp 
[WARNING] Don't override file /Users/workspace/tapestry/Myapp/src/test/resource 
[WARNING] Don't override file /Users/workspace/tapestry/Myapp/src/test/conf 
[WARNING] Don't override file /Users/workspace/tapestry/Myapp/src/site 
[INFO] project created from Archetype in dir: /Users/workspace/tapestry/Myapp 
[INFO] --------------------------------------------------------------------------------- 
[INFO] BUILD SUCCESS 
[INFO] --------------------------------------------------------------------------------- 
[INFO] Total time: 11:28 min 
[INFO] Finished at: 2016-09-14T00:47:23+05:30 
[INFO] Final Memory: 14M/142M 
[INFO] ---------------------------------------------------------------------------------

到这里,您已经成功构建了 Tapestry Quick Start 项目。使用以下命令移动到新创建的Myapp目录的位置并开始编码。

cd Myapp 

运行应用程序

要运行骨架项目,请使用以下命令。

mvn jetty:run -Dtapestry.execution-mode=development

你会得到这样的屏幕,

[INFO] Scanning for projects... 
[INFO] 
[INFO] ---------------------------------------------------------------------------------
[INFO] Building Myapp Tapestry 5 Application 1.0-SNAPSHOT 
[INFO] ---------------------------------------------------------------------------------
........ 
........ 
........ 
Application 'app' (version 1.0-SNAPSHOT-DEV) startup time: 346 ms to build IoC 
Registry, 1,246 ms overall.  
 ______                  __             ____ 
/_  __/__ ____  ___ ___ / /_______ __  / __/ 
 / / / _ `/ _ \/ -_|_-</ __/ __/ // / /__ \  
/_/  \_,_/ .__/\__/___/\__/_/  \_, / /____/ 
        /_/                   /___/  5.4.1 (development mode)   
[INFO] Started SelectChannelConnector@0.0.0.0:8080 
[INFO] Started Jetty Server

到目前为止,我们已经在 Tapestry 中创建了一个基本的快速入门项目。要在 Web 浏览器中查看正在运行的应用程序,只需在地址栏中输入以下 URL 并按 Enter 键 -

https://localhost:8080/myapp

这里,myapp是应用程序的名称,开发模式下应用程序的默认端口是8080。

使用Eclipse

在上一章中,我们讨论了如何在 CLI 中创建 Tapestry Quick Start 应用程序。本章介绍如何在Eclipse IDE中创建应用程序框架。

让我们使用 Maven 原型来创建骨架应用程序。要配置新应用程序,您可以按照以下步骤操作。

第1步:打开Eclipse IDE

打开 Eclipse 并选择 File → New → Project… → 选项,如以下屏幕截图所示。

打开Eclipse

现在,选择 Maven → Maven 项目选项。

注意- 如果未配置 Maven,则配置并创建一个项目。

选择 Maven 项目后,单击“下一步”,然后再次单击“下一步”按钮。

Maven项目

之后,您将看到一个屏幕,您应该在其中选择配置选项。配置完成后,您将看到以下屏幕。

配置选项

第 2 步:目录配置

第一步完成后,您应该单击“添加远程目录”。然后添加以下更改,如以下屏幕截图所示。

远程目录

现在,添加了 Apache Tapestry 目录。然后,选择过滤器选项 org.apache.tapestry Quickstart 5.4.1,如下所示。

添加目录

然后单击“下一步”,将出现以下屏幕。

组 ID 字段

步骤3:配置GroupId、ArtifactId、版本和包

将以下更改添加到 Tapestry 目录配置中。

更改配置

然后单击“完成”按钮,现在我们已经创建了第一个骨架应用程序。第一次使用 Maven 时,项目创建可能需要一段时间,因为 Maven 会下载 Maven、Jetty 和 Tapestry 的许多 JAR 依赖项。Maven 完成后,您将在 Package Explorer 视图中看到一个新目录 MyFirstApplication。

步骤 4:使用 Jetty 服务器运行应用程序

您可以使用Maven直接运行Jetty。右键单击 Package Explorer 视图中的 MyFirstApplication 项目,然后选择 Run As → Maven Build...,您将看到如下所示的屏幕。

码头服务器

在配置对话框中,输入目标选项“jetty:run”,然后单击“运行”按钮。

目标选项

Jetty 初始化后,您将在控制台中看到以下屏幕。

码头初始化

第 5 步:在 Web 浏览器中运行

输入以下 URL 以在 Web 浏览器中运行该应用程序 –

https://loclhost:8080/MyFirstApplication

运行网络浏览器

第 6 步:停止 Jetty 服务器

要停止 Jetty 服务器,请单击控制台中的红色方形图标,如下所示。

停止服务器

Apache Tapestry - 项目布局

以下是Maven Quickstart CLI创建的源代码的布局。此外,这是标准 Tapestry 应用程序的建议布局。

├── build.gradle 
├── gradle 
│   └── wrapper 
│       ├── gradle-wrapper.jar 
│       └── gradle-wrapper.properties 
├── gradlew 
├── gradlew.bat 
├── pom.xml 
├── src 
│   ├── main 
│   │   ├── java 
│   │   │   └── com 
│   │   │       └── example 
│   │   │           └── MyFirstApplication 
│   │   │               ├── components 
│   │   │               ├── data 
│   │   │               ├── entities 
│   │   │               ├── pages 
│   │   │               └── services 
│   │   ├── resources 
│   │   │   ├── com 
│   │   │   │   └── example 
│   │   │   │       └── MyFirstApplication 
│   │   │   │           ├── components 
│   │   │   │           ├── logback.xml 
│   │   │   │           └── pages 
│   │   │   │               └── Index.properties  
│   │   │   ├── hibernate.cfg.xml 
│   │   │   └── log4j.properties
│   │   └── webapp 
│   │       ├── favicon.ico 
│   │       ├── images 
│   │       │   └── tapestry.png 
│   │       ├── mybootstrap 
│   │       │   ├── css 
│   │       │   │   ├── bootstrap.css 
│   │       │   │   └── bootstrap-theme.css 
│   │       │   ├── fonts 
│                   ├── glyphicons-halflings-regular.eot 
│   │       │   │   ├── glyphicons-halflings-regular.svg 
│   │       │   │   ├── glyphicons-halflings-regular.ttf 
│   │       │   │   ├── glyphicons-halflings-regular.woff 
│   │       │   │   └── glyphicons-halflings-regular.woff2 
│   │       │   └── js 
│   │       └── WEB-INF 
│   │           ├── app.properties 
│   │           └── web.xml 
│   ├── site 
│   │   ├── apt 
│   │   │   └── index.apt 
│   │   └── site.xml 
│   └── test 
│       ├── conf 
│       │   ├── testng.xml 
│       │   └── webdefault.xml 
│       ├── java 
│       │   └── PLACEHOLDER 
│       └── resources 
│           └── PLACEHOLDER 
└── target     
   ├── classes     
   │   ├── com  
   │   │   └── example
   │   │       └── MyFirstApplication     
   │   │           ├── components     
   │   │           ├── data     
   │   │           ├── entities     
   │   │           ├── logback.xml     
   │   │           ├── pages 
   │   │           │   └── Index.properties 
   │   │           └── services     
   │   ├── hibernate.cfg.xml 
   │   └── log4j.properties     
   ├── m2e-wtp 
   │   └── web-resources 
   │       └── META-INF     
   │           ├── MANIFEST.MF 
   │           └── maven 
   │               └── com.example 
   │                   └──MyFirstApplication     
   │                     ├── pom.properties 
   │                       └── pom.xml     
   ├── test-classes 
   │   └── PLACEHOLDER 
   └── work         
      ├── jsp         
      ├── sampleapp.properties 
      └── sampleapp.script

默认布局的排列方式类似于WAR 内部文件格式。使用 WAR 格式有助于无需打包和部署即可运行应用程序。此布局只是一个建议,但如果在部署时将应用程序打包为正确的 WAR 格式,则应用程序可以以任何格式进行排列。

源代码可以分为以下四个主要部分。

  • Java 代码- 所有 java 源代码都放置在/src/main/java文件夹下。Tapestry 页面类放置在“Pages”文件夹下,Tapestry 组件类放置在 Components 文件夹下。Tapestry 服务类位于 services 文件夹下。

  • ClassPath 资源- 在 Tapestry 中,大多数类都有关联的资源(XML 模板、JavaScript 文件等)。这些资源位于/src/main/resources文件夹下。Tapestry 页面类在“Pages”文件夹下有其关联的资源,Tapestry 组件类在 Components 文件夹下有其关联的资源。这些资源被打包到WAR的WEB-INF/classes文件夹中。

  • 上下文资源- 它们是 Web 应用程序的静态资源,例如图像、样式表和 JavaScript 库/模块。它们通常放置在 /src/main/webapp文件夹下,称为Context Resources。另外,Web 应用程序描述文件(Java Servlet 的)web.xml 放置在上下文资源的WEB-INF文件夹下。

  • 测试代码- 这些是用于测试应用程序的可选文件,并放置在src/test/javasrc/test/资源文件夹下。它们没有打包到 WAR 中。

约定优于配置

Apache Tapestry 在编程的各个方面都遵循约定优于配置。该框架的每个功能都有一个合理的默认约定。

例如,正如我们在“项目布局”一章中了解到的,所有页面都需要放置在/src/main/java/«package_path»/pages/文件夹中才能被视为 Tapestry 页面。

从另一种意义上说,不需要将特定的 Java 类配置为 Tapestry 页面。将类放置在预定义的位置就足够了。在某些情况下,遵循 Tapestry 的默认约定会很奇怪。

例如,Tapestry 组件可以有一个setupRender方法,该方法将在渲染阶段开始时触发。开发人员可能想使用他们自己的固定名称,例如initializeValue。在这种情况下,Tapestry 提供了Annotation来覆盖约定,如以下代码块所示。

void setupRender() { 
   // initialize component 
}  
@SetupRender 
void initializeValue() { 
   // initialize component 
}

这两种编程方式在 Tapestry 中都有效。简而言之,Tapestry 的默认配置非常简单。只需在“Web.xml”中配置Apache Tapestry 过滤器(Java Servlet 过滤器)即可确保应用程序正常工作。

Tapestry 提供了另一种配置应用程序的方法,称为AppModule.java

Apache Tapestry - 注释

注释是 Tapestry 用来简化 Web 应用程序开发的一个非常重要的功能。Tapestry 提供了很多自定义注释。它有类、方法和成员字段的注释。正如上一节中所讨论的,注释还可以用于覆盖功能的默认约定。Tapestry 注释分为四个主要类别,如下所示。

组件注释

用于页面、组件和 Mixins 类。一些有用的注释是 -

  • @Property - 它适用于字段。用于将字段转换为 Tapestry 属性。

  • @Parameter - 它适用于字段。用于指定一个字段作为组件的参数。

  • @Environmental - 它适用于领域。用于在不同组件之间共享私有字段。

  • @import - 它适用于类和字段。用于包含资源、CSS 和 JavaScript。

  • @Path - 与 @Inject 注释结合使用,根据路径注入资产。

  • @Log - 它适用于类和字段。用于调试目的。可用于发出组件的事件信息,如事件开始、事件结束等。

IoC注释

用于将对象注入到 IoC 容器中。一些有用的注释是 -

  • @Inject - 它适用于字段。用于标记应该注入到IoC容器中的参数。它标记应该注入到组件中的字段。

  • @Value - 它适用于字段。与 @inject 注释一起使用来注入文字值而不是服务(这是 @Inject 注释的默认Behave)。

数据保存类的注释

它用于为高级组件指定类(通常是模型或数据保存类)中的组件特定信息,例如

  • 网格(用于创建高级表格数据,例如报告、图库等)

  • BeanEditForm(用于创建高级表单)

  • Hibernate(用于高级数据库访问)等

这些注释被聚合并打包到一个单独的 jar 中,没有任何挂毯依赖性。一些注释是 -

  • @DataType - 用于指定字段的数据类型。Tapestry 组件可以使用此信息在表示层中创建设计或标记。

  • @Validate - 用于指定字段的验证规则。

这些分隔使 Tapestry 应用程序能够使用多层设计

Apache Tapestry - 页面和组件

Tapestry 应用程序只是 Tapestry 页面的集合。它们共同构成一个定义明确的 Web 应用程序。每个页面都会有一个相应的 XML 模板和零个、一个或多个组件。页面和组件是相同的,只是页面是根组件并且通常由应用程序开发人员创建。

组件是根 PageComponent 的子组件。Tapestry 有许多内置组件,并且可以选择创建自定义组件。

页面组件

页数

如前所述,页面是 Tapestry 应用程序的构建块。页面是普通的 POJO,放置在 – /src/main/java/«package_path»/pages/文件夹下。每个页面都会有一个相应的XML 模板,其默认位置是 - /src/main/resources/«package_name»/pages/

您可以在此处看到页面和模板的路径结构类似,只是模板位于资源文件夹中。

例如,包名称为com.example.MyFirstApplication的 Tapestry 应用程序中的用户注册页面将具有以下页面和模板文件 -

  • Java 类-

    /src/main/java/com/example/MyFirstApplication/pages/index.java

  • XML 模板-

    /src/main/resources/com/example/MyFirstApplication/pages/index.tml

让我们创建一个简单的Hello World页面。首先,我们需要在 – /src/main/java/com/example/MyFirstApplication/pages/HelloWorld.java 创建一个Java 类

package com.example.MyFirstApplication.pages; 
public class HelloWorld { 
}

然后,创建一个 XML 模板 –

“/src/main/resources/com/example/MyFirstApplication/pages/helloworld.html”。

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

现在,可以通过https://localhost:8080/myapp/helloworld访问此页面。这是一个简单的挂毯页面。Tapestry 提供了更多用于开发动态网页的功能,我们将在下面的章节中讨论这些功能。

Apache Tapestry - 模板

让我们考虑本节中的 Tapestry XML 模板。XML 模板是格式良好的 XML 文档。页面的表示(用户界面)层是 XML 模板。除了下面给出的项目之外,XML 模板还具有正常的 HTML 标记 -

  • Tapestry命名空间
  • 扩展
  • 元素
  • 成分

现在让我们详细讨论它们。

Tapestry命名空间

Tapestry 命名空间只不过是 XML 命名空间。命名空间应在模板的根元素中定义。它用于在模板中包含 Tapestry 组件和组件相关信息。最常用的命名空间如下 -

  • xmlns:t = “https://tapestry.apache.org/schema/tapestry_5_4.xsd” — 用于标识 Tapestry 的元素、组件和属性。

  • xmlns:p = “tapestry:parameter” — 用于将任意代码块传递给组件。

Tapestry 命名空间的示例如下 -

<html xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_3.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   <head> 
      <title>Hello World Page</title> 
   </head>  
   <body> 
      <h1>Hello World</h1> 
      <t:eventlink page = "Index">refresh page</t:eventlink> 
   </body> 
</html>

扩展

扩展是在页面呈现阶段动态更改 XML 模板的简单而有效的方法。扩展使用 ${<name>} 语法。XML 模板中有多种表达扩展的选项。让我们看看一些最常用的选项 -

财产扩张

它映射相应 Page 类中定义的属性。它遵循 Java 类中属性定义的 Java Bean 规范。它更进一步,忽略了属性名称的大小写。让我们使用属性扩展来更改“Hello World”示例。以下代码块是修改后的Page类。

package com.example.MyFirstApplication.pages; 
public class HelloWorld {   
   // Java Bean Property 
   public String getName { 
      return "World!"; 
   } 
}

然后,更改相应的 XML 模板,如下所示。

<html xmlns:t = "http://tapestry.apache.org/schema/tapestry_5_4.xsd"> 
   <head> 
      <title>Hello World Page</title> 
   </head> 
   <body> 
      <!-- expansion --> 
      <h1>Hello ${name}</h1> 
   </body> 
</html>

在这里,我们在 Page 类中将name定义为Java Bean 属性,并使用扩展${name}在 XML 模板中动态处理它。

消息扩展

每个页面类可能有也可能没有关联的属性文件 -资源文件夹中的“page_name”.properties 。属性文件是纯文本文件,每行具有单个键/值对(消息)。让我们为 HelloWorld 页面创建一个属性文件:

“/src/main/resources/com/example/MyFirstApplication/pages/helloworld.properties”并添加“Greeting”消息。

Greeting = Hello

问候消息可以在 XML 模板中使用${message:greeting }

<html xmlns:t = "http://tapestry.apache.org/schema/tapestry_5_4.xsd"> 
   <head> 
      <title>Hello World Page</title> 
   </head> 
   <body> 
      <!-- expansion --> 
      <h1>${message:greeting} ${name}</h1> 
   </body> 
</html>

元素

Tapestry 有一小部分可在 XML 模板中使用的元素。元素是在 Tapestry 命名空间下定义的预定义标签 -

https://tapestry.apache.org/schema/tapestry_5_4.xsd

每个元素都是为了特定目的而创建的。可用的挂毯元素如下 -

<t:正文>

当两个组件嵌套时,父组件的模板可能必须包裹子组件的模板。<t:body> 元素在这种情况下很有用。<t:body> 的用途之一是在模板布局中。

一般来说,Web 应用程序的用户界面都会有通用的页眉、页脚、菜单等。这些通用项在 XML 模板中定义,称为模板布局或布局组件。在 Tapestry 中,它需要由应用程序开发人员创建。布局组件只是另一个组件,放置在组件文件夹下,该文件夹具有以下路径 - src/main/«java|resources»/«package_name»/components

让我们创建一个名为MyCustomLayout的简单布局组件。MyCustomLayout 的代码如下 -

<!DOCTYPE html> 
<html xmlns:t = "http://tapestry.apache.org/schema/tapestry_5_4.xsd"> 
   <head> 
      <meta charset = "UTF-8" />
      <title>${title}</title>  
   </head> 
   <body> 
      <div>Sample Web Application</div> 
      <h1>${title}</h1> 
      <t:body/> 
      
      <div>(C) 2016 TutorialsPoint.</div> 
   </body> 
</html> 

package com.example.MyFirstApplication.components;  

import org.apache.tapestry5.*; 
import org.apache.tapestry5.annotations.*; 
import org.apache.tapestry5.BindingConstants;  

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

在 MyCustomLayout 组件类中,我们声明了一个标题字段,并通过使用注释将其设为强制字段。现在,更改 HelloWorld.html 模板以使用我们的自定义布局,如下面的代码块所示。

<html>
   t:type = "mycustomlayout" title = "Hello World Test page"
      xmlns:t = "http://tapestry.apache.org/schema/tapestry_5_4.xsd"> 
   <h1>${message:greeting} ${name}</h1> 
</html>

我们可以在这里看到 XML 模板没有 head 和 body 标签。Tapestry 将从布局组件收集这些详细信息,并且布局组件的 <t:body> 将被 HelloWorld 模板替换。一切完成后,Tapestry 将发出类似的标记,如下所示 -

<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset = "UTF-8" /> 
      <title>Hello World Test Page</title> 
   </head> 
   <body> 
      <div>Sample Web Application</div> 
      <h1>Hello World Test Page</h1> 
      <h1>Hello World!</h1> 
      <div>(C) 2016 TutorialsPoint.</div> 
   </body> 
</html>

布局可以嵌套。例如,我们可以通过包含管理功能来扩展自定义布局,并将其用于管理部分,如下所示。

<html t:type = "MyCommonLayout" 
   xmlns:t = "http://tapestry.apache.org/schema/tapestry_5_4.xsd"> 
   
   <div><!-- Admin related items --><div> 
   <t:body/> 
  
</html>

<t:容器>

<t:container> 是顶级元素,包含 Tapestry 命名空间。这用于指定组件的动态部分。

例如,网格组件可能需要一个模板来识别如何在 HTML 表中呈现其行 tr(和列 td)。

<t:container xmlns:t = "http://tapestry.apache.org/schema/tapestry_5_4.xsd"> 
   <td>${name}</td> 
   <td>${age}</td> 
</t:container>

<t:块>

<t:block> 是模板中动态部分的占位符。一般来说,块元素不会渲染。仅模板中定义的组件使用块元素。组件将动态地将数据注入块元素并渲染它。流行的用例之一是AJAX

块元素为要呈现的动态数据提供准确的位置和标记。每个块元素都应该有一个相应的 Java 属性。只有这样才能动态渲染。块元素的id应该遵循Java变量标识符规则。下面提供了部分示例。

@Inject 
private Block block;  
<html t:type = "mycustomlayout" title = "block example" 
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter">  
<h1>${title}</h1>  
<!--  
   ... 
   ...  
--> 
<t:block t:id = "block"> 
   <h2>Highly dynamic section</h2> 
   I'v been updated through AJAX call 
   The current time is: <strong>${currentTime}</strong>
</t:block>  
<!--  
   ... 
   ...  
-->  
</html>

<t:内容>

<t:content> 元素用于指定模板的实际内容。一般来说,所有标记都被视为模板的一部分。如果指定了 <t:content>,则仅考虑其中的标记。设计人员使用此功能来设计没有布局组件的页面。

<t:删除>

<t:remove> 与内容元素正好相反。删除元素内的标记不被视为模板的一部分。它可用于仅服务器注释和设计目的。

资产

资产是静态资源文件,例如样式表、图像和 JavaScript 文件。通常,资源放置在 Web 应用程序根目录/src/main/webapp中。

<head> 
   <link href = "/css/site.css" rel = "stylesheet" type = "text/css"/>

Tapestry 还将存储在Java 类路径中的文件视为资产。Tapestry 提供了高级选项,可通过扩展选项将资源包含到模板中。

  • 上下文- 在网络上下文中获取可用资产的选项。

<img src = "${context:image/tapestry_banner.gif}" alt = "Banner"/>

asset - 组件通常将其自己的资产与 Java 类一起存储在 jar 文件中。从 Tapestry 5.4 开始,类路径中存储资源的标准路径是META-INF/assets。对于库,存储资源的标准路径是META-INF/assets/«library_name»/。asset:还可以调用context:扩展来从 Web 上下文中获取资产。

<img src = "${asset:context:image/tapestry_banner.gif}" alt = "Banner"/>

可以使用注入和路径注释将资源注入到 Tapestry 页面或组件中。Path注释的参数是资产的相对路径。

@Inject 
@Path("images/edit.png") 
private Asset icon;

Path参数还可以包含AppModule.java部分中定义的 Tapestry 符号。

例如,我们可以定义一个符号,skin.root,其值为 context:skins/basic 并按如下所示使用它 -

@Inject 
@Path("${skin.root}/style.css") 
private Asset style;

本土化

通过挂毯包含资源可提供额外的功能。其中一项功能是“本地化”。Tapestry 将检查当前区域设置并包含适当的资源。

例如,如果当前语言环境设置为de,则将包含edit_de.png而不是 edit.png。

CSS

Tapestry 有内置的样式表支持。Tapestry 将注入Tapestry.css作为核心 Javascript 堆栈的一部分。从 Tapestry 5.4 开始,tapestry还包含bootstrap css 框架。我们可以使用普通的链接标签包含我们自己的样式表。在这种情况下,样式表应位于 Web 根目录 - /src/main/webapp/中。

<head> 
   <link href = "/css/site.css" rel = "stylesheet" type = "text/css"/>

Tapestry 提供了高级选项,可通过前面讨论的扩展选项将样式表包含到模板中。

<head> 
   <link href = "${context:css/site.css}" rel = "stylesheet" type = "text/css"/> 

Tapestry 还提供 Import 注释,将样式表直接包含到 Java 类中。

@Import(stylesheet="context:css/site.css") 
public class MyCommonLayout { 
} 

Tapestry 提供了很多选项来通过 AppModule.java 管理样式表。一些重要的选项是 -

  • Tapestry 默认样式表可能会被删除。

@Contribute(MarkupRenderer.class) 

public static void 
deactiveDefaultCSS(OrderedConfiguration<MarkupRendererFilter> configuration) { 
   configuration.override("InjectDefaultStyleheet", null); 
} 
  • 还可以通过覆盖其路径来禁用 Bootstrap。

configuration.add(SymbolConstants.BOOTSTRAP_ROOT, "classpath:/METAINF/assets");
  • 启用资产(CSS 和 JavaScript)的动态最小化。我们还需要包含Tapestry-webresources依赖项(在 pom.xml 中)。

@Contribute(SymbolProvider.class) 
@ApplicationDefaults 

public static void contributeApplicationDefaults( 
   MappedConfiguration<String, String> configuration) { 
   
   configuration.add(SymbolConstants.MINIFICATION_ENABLED, "true"); 
} 

<dependency> 
   <groupId>org.apache.tapestry</groupId> 
   <artifactId>tapestry-webresources</artifactId> 
   <version>5.4</version> 
</dependency> 

客户端 JavaScript

当前一代的 Web 应用程序严重依赖 JavaScript 来提供丰富的客户端体验。Tapestry 承认这一点并为 JavaScript 提供一流的支持。JavaScript 支持已深深融入到 Tapestry 中,并且在编程的每个阶段都可用。

早些时候,Tapestry 仅支持 Prototype 和 Scriptaculous。但是,从版本 5.4 开始,tapestry 完全重写了 JavaScript 层,使其尽可能通用,并为 JQuery(事实上的 JavaScript 库)提供一流的支持。此外,tapestry 鼓励基于模块的 JavaScript 编程并支持 RequireJS,这是 AMD 的一种流行的客户端实现(异步模块定义 - 以异步方式支持模块及其依赖项的 JavaScript 规范)。

地点

JavaScript 文件是 Tapestry 应用程序的资产。根据资产规则,JavaScript 文件放置在 Web 上下文/sr/main/webapp/下,或者放置在META-INF/assets/ location下的 jar 内。

链接 JavaScript 文件

在 XML 模板中链接 JavaScript 文件的最简单方法是直接使用 script 标签,即<script language = "javascript" src = "relative/path/to/js"></script>。但是,Tapestry 不推荐这些方法。Tapestry 提供了多个选项来直接在页面/组件本身中链接 JavaScript 文件。下面给出了其中一些。

  • @import 注释- @import 注释提供了使用上下文表达式链接多个 JavaScript 库的选项。它可以应用于 Page 类及其方法。如果应用于 Page 类,则它应用于其所有方法。如果应用于页面的方法,则它仅应用于该方法,然后 Tapestry 仅在调用该方法时才链接 JavaScript 库。

@Import(library = {"context:js/jquery.js","context:js/myeffects.js"}) 

public class MyComponent { 
   // ... 
}
  • JavaScriptSupport 接口- JavaScriptSupport 是 Tapestry 定义的接口,它有一个方法importJavaScriptLibrary来导入 JavaScript 文件。只需使用 @Environmental 注释进行声明和注释即可轻松创建 JavScriptSupport 对象。

@Inject @Path("context:/js/myeffects.js") 
private Asset myEffects;  

@Environmental 
private JavaScriptSupport javaScriptSupport;  
void setupRender() { 
   javaScriptSupport.importJavaScriptLibrary(myEffects); 
}
  • JavaScripSupport 只能使用@Environmental注释注入到组件中。对于服务,我们需要使用@Inject注解或将其添加为服务构造函数方法中的参数。

@Inject 
private JavaScriptSupport javaScriptSupport; 
public MyServiceImpl(JavaScriptSupport support) { 
   // ... 
}
  • addScript 方法- 这与 JavaScriptSupport 接口类似,只是它使用addScript方法并且代码直接添加到页面底部的输出中。

void afterRender() { 
   javaScriptSupport.addScript(
      "$('%s').observe('click', hideMe());", container.getClientId()); 
}

JavaScript 堆栈

Tapestry 允许将一组 JavaScript 文件和相关样式表组合起来并用作一个单独的实体。目前,Tapestry 包括基于 Prototype 和基于 JQuery 的堆栈。

开发人员可以通过实现JavaScriptStack接口来开发自己的堆栈并将其注册到AppModule.java中。注册后,可以使用@import注释导入堆栈。

@Contribute(JavaScriptStackSource.class) 
public static void addMyStack(
   MappedConfiguration<String, JavaScriptStack> configuration) { 
   
   configuration.addInstance("MyStack", myStack.class); 
}  

@Import(stack = "MyStack") 
public class myPage { 
}

Apache Tapestry - 组件

如前所述,组件和页面是相同的,只是页面是根组件并包含一个或多个子组件。组件始终驻留在页面内,并执行页面的几乎所有动态功能。

Tapestry 组件通过交互式 AJAX呈现简单的 HTML 链接到复杂的网格功能。一个组件也可以包含另一个组件。Tapestry 组件由以下项目组成 -

  • 组件类- 组件的主要 Java 类。

  • XML 模板- XML 模板类似于页面模板。组件类将模板呈现为最终输出。有些组件可能没有模板。在这种情况下,输出将由组件类本身使用MarkupWriter类生成。

  • Body - 页面模板内指定的组件可能具有自定义标记,称为“组件主体”。如果组件模板有<body />元素,则 <body /> 元素将被组件的主体替换。这与前面在 XML 模板部分中讨论的布局类似。

  • 渲染- 渲染是将组件的 XML 模板和主体转换为组件的实际输出的过程。

  • 参数- 用于在组件和页面之间创建通信,从而在它们之间传递数据。

  • 事件- 将组件的功能委托给其容器/父级(页面或其他组件)。它广泛用于页面导航目的。

渲染

组件的渲染是在一系列预定义的阶段中完成的。组件系统中的每个阶段都应该在组件类中通过约定或注释定义相应的方法。

// Using annotaion 
@SetupRender 
void initializeValues() { 
   // initialize values 
}

// using convention 
boolean afterRender() { 
   // do logic 
   return true; 
}

下面列出了各个阶段、其方法名称及其注释。

注解 默认方法名称
@SetupRender 设置渲染()
@BeginRender 开始渲染()
@BeforeRenderTemplate beforeRenderTemplate()
@BeforeRenderBody beforeRenderBody()
@AfterRenderBody afterRenderBody()
@AfterRenderTemplate afterRenderTemplate()
@渲染后 渲染后()
@CleanupRender 清理渲染()

每个阶段都有特定的目的,如下 -

设置渲染

SetupRender 启动渲染过程。它通常设置组件的参数。

开始渲染

BeginRender 开始渲染组件。它通常呈现组件的开始/开始标签。

渲染模板之前

BeforeRenderTemplate 用于装饰 XML 模板,在模板周围添加特殊标记。它还提供了跳过模板渲染的选项。

渲染主体之前

BeforeRenderTemplate 提供了一个选项来跳过组件主体元素的渲染。

渲染后主体

AfterRenderBody 将在组件主体渲染后调用。

渲染后模板

AfterRenderTemplate 将在渲染组件的模板后调用。

渲染后

AfterRender 与 BeginRender 相对应,通常渲染关闭标记。

清理渲染

CleanupRender 是SetupRender 的对应部分。它释放/处置渲染过程中创建的所有对象。

渲染阶段的流程不仅仅是向前的。它根据阶段的返回值在阶段之间来回移动。

例如,如果SetupRender方法返回false,则渲染将跳转到CleanupRender阶段,反之亦然。为了清楚地了解不同阶段之间的流程,请检查下图中的流程。

注释列表

简单组件

让我们创建一个简单的组件 Hello,其输出消息为“Hello, Tapestry”。以下是 Hello 组件及其模板的代码。

package com.example.MyFirstApplication.components;  
public class Hello {  
}
<html  
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
  
   <div> 
      <p>Hello, Tapestry (from component).</p> 
   </div> 
  
</html>

Hello 组件可以在页面模板中调用如下:

<html title = "Hello component test page" 
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
<t:hello />  
</html>

同样,组件可以使用 MarkupWriter 而不是模板来呈现相同的输出,如下所示。

package com.example.MyFirstApplication.components; 
  
import org.apache.tapestry5.MarkupWriter; 
import org.apache.tapestry5.annotations.BeginRender;   

public class Hello { 
   @BeginRender 
   void renderMessage(MarkupWriter writer) { 
      writer.write("<p>Hello, Tapestry (from component)</p>"); 
   } 
}

让我们更改组件模板并包含 <body /> 元素,如下面的代码块所示。

<html>  
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   <div> 
      <t:body /> 
   </div> 
</html>

现在,页面模板可以在组件标记中包含主体,如下所示。

<html title = "Hello component test page" 
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   <t:hello> 
      <p>Hello, Tapestry (from page).</p> 
   </t:hello> 
</html>

输出如下 -

<html> 
   <div> 
      <p>Hello, Tapestry (from page).</p> 
   </div> 
</html>

参数

这些参数的主要目的是在组件的字段和页面的属性/资源之间创建连接。使用参数,组件与其对应的页面之间进行通信并传输数据。这称为双向数据绑定

例如,用户管理页面中用于表示年龄的文本框组件通过该参数获取其初始值(在数据库中可用)。同样,在用户的年龄更新并提交回来后,组件将通过相同的参数发送回更新后的年龄。

要在组件类中创建新参数,请声明一个字段并指定@Parameter注释。这个@Parameter有两个可选参数,它们是 -

  • required - 使参数成为强制参数。如果未提供 Tapestry,则会引发异常。

  • value - 指定参数的默认值。

该参数应在页面模板中指定为组件标签的属性。属性的值应该使用绑定表达式/扩展来指定,我们在前面的章节中讨论过。我们之前学到的一些扩展是 -

  • 属性扩展 (prop:«val») - 从页面类的属性中获取数据。

  • 消息扩展(消息:«val») - 从index.properties文件中定义的键获取数据。

  • 上下文扩展 (context:«val») - 从 Web 上下文文件夹 /src/main/webapp 获取数据。

  • 资产扩展 (asset:«val») - 从 jar 文件 /META-INF/assets 中嵌入的资源中获取数据。

  • 符号扩展 (symbol:«val») - 从 AppModule.java 文件中定义的符号获取数据。

Tapestry 有许多更有用的扩展,其中一些如下所示 -

  • 文字扩展 (literal:«val») - 文字字符串。

  • Var 扩展 (var:«val») - 允许读取或更新组件的渲染变量。

  • 验证扩展 (validate:«val») - 用于指定对象的验证规则的专用字符串。例如,验证:必需,minLength = 5。

  • Translate (translate:«val») - 用于在输入验证中指定 Translator 类(将客户端表示转换为服务器端表示)。

  • Block (block:«val») - 模板内块元素的 id。

  • Component (component:«val») - 模板中另一个组件的 id。

除Property 扩展和Var 扩展外,上述所有扩展都是只读的。组件使用它们与页面交换数据。当使用扩展作为属性值时,不应使用${...} 。相反,只需使用不带美元和大括号符号的扩展即可。

组件使用参数

让我们创建一个新组件 HelloWithParameter,方法是修改 Hello 组件,通过在组件类中添加名称参数并相应地更改组件模板和页面模板来动态呈现消息。

  • 创建一个新的组件类HelloWithParameter.java

  • 添加私有字段并使用@Parameter注释对其进行命名。使用必需的参数使其成为强制性的。

@Parameter(required = true) 
private String name;
  • 添加私有字段,结果带有@Propery注释。结果属性将在组件模板中使用。组件模板无法访问@Parameter注解的字段,只能访问@Property注解的字段。组件模板中可用的变量称为渲染变量。

@Property 
 private String result;
  • 添加 RenderBody 方法并将名称参数中的值复制到结果属性。

@BeginRender 
void initializeValues() { 
   result = name; 
}
  • 添加新的组件模板HelloWithParamter.tml并使用 result 属性来呈现消息。

<div> Hello, ${result} </div>
  • 在测试页面 (testhello.java) 中添加一个新属性 Username。

public String getUsername() { 
   return "User1"; 
}
  • 在页面模板中使用新创建的组件,并在HelloWithParameter组件的 name 参数中设置 Username 属性。

<t:helloWithParameter name = "username" /> 

完整列表如下 -

package com.example.MyFirstApplication.components;  

import org.apache.tapestry5.annotations.*;  
public class HelloWithParameter { 
   @Parameter(required = true) 
   private String name; 
     
   @Property 
   private String result; 
   
   @BeginRender 
   void initializeValues() { 
      result = name; 
   } 
}
<html  
   xmlns:t = "https://tapestry.apache.org/schema/tapestry_5_4.xsd" 
   xmlns:p = "tapestry:parameter"> 
   
   <div> Hello, ${result} </div> 
  
</html>
package com.example.MyFirstApplication.pages;  

import org.apache.tapestry5.annotations.*;  
public class TestHello { 
   public String getUsername() { 
      return "User1"; 
   } 
}