Jigloo 是一个 Eclipse 插件,使您可以快速构建在 Java? 平台上运行的复杂图形用户界面 (GUI)。它可用于构建基于 Swing 的应用程序和基于 Standard Widget Toolkit (SWT) 的应用程序。它是一个易于使用的可视化编辑器,因此您可以为桌面应用程序快速创建 UI。在本教程中,将构建一个简单的工作流应用程序并将使用 Jigloo 创建它的 UI。了解开始使用 Jigloo 并进而使用其高级功能(例如可视化继承)是多么轻松。最后,我们将测试应用程序并将其打包以供其他人使用。 开始之前 本教程适用于想要构建桌面应用程序并使用名为 Jigloo 的 Eclipse 插件来为其应用程序创建 UI 的 Java 开发人员。样例应用程序将使用 XML、XML Schema 和 JAXB 以及一些 Java 5 功能,例如 Annotation 和 Generic。 关于本教程 Java 是构建富桌面应用程序的优秀平台。当 Java 于 1995 年出现时,它附带了 Abstract Window Toolkit (AWT)。这曾是 Java 的第一个用于构建桌面应用程序的 UI 库。1998 年发行的 JDK 1.2 包括 Swing,一个有很大改进的工具包。在那之后,还对 Swing 进行了多次改进。它现在是一个功能强大的 UI 库,可在多种不同的平台上良好运行。SWT 是一个适用于 Java 的非常有竞争力的 UI 工具包,它可提供很多优点。现在使用 Jigloo,您可以快速构建以 Swing 或 SWT 为目标的 UI。您甚至可以构建包括 Swing 组件的 SWT 应用程序,但是那超出了本教程的讨论范围。 在本教程中,您将通过构建简单的工作流应用程序来了解 Jigloo。您将使用 Eclipse 插件 Jigloo 来为应用程序创建 UI。然后将构建和测试应用程序,并将其打包以供其他人使用。 先决条件 熟悉事件处理程序和数据绑定的 UI 编程概念将非常有用,但不是绝对必要的。了解 AWT/Swing 或 SWT 也会十分有用,但不作要求。 系统需求 Eclipse V3.2 由于 Jigloo 是 Eclipse 的插件,因此您当然需要 Eclipse。应用程序将使用 SWT,并且 Eclipse 还包括所需的 SWT 库。 Jigloo Jigloo 是 Eclipse 的插件。在教程中,您将看到如何使用 Eclipse 强大的更新管理器从 Eclipse 直接安装 Jigloo。 Java 5+ 本教程中的应用程序将使用一些 Java 5 功能,例如 Annotation 和 Generic。下载 Java 5 或 Java 6。 JAXB V2.0 本教程中的应用程序将使用 XML 作为数据存储,并将使用 JAXB 解析和序列化 XML。如果使用的是 Java 6,则已经包括了 JAXB 并且不需要做其他操作。如果使用的是 Java 5,您将需要使用 Sun 的 JAXB 2.0 Reference Implementation (RI)。 概述 在这一节中,先查看 Jigloo 插件以及您可以使用它完成哪些任务,然后再执行它的安装和设置步骤。 什么是 Jigloo? Jigloo 是一个可视化 Java GUI 生成器。它是 CloudGarden 开发的 Eclipse(和 WebSphere? Studio)插件。它可以免费用于非商业应用。要将 Jigloo 用于商业应用,您需要从 CloudGarden 获得专业许可证。 使用 Jigloo 可以完成哪些任务? Jigloo 是一个典型的 WYSIWYG 编辑器,用于构建 Swing 和 SWT GUI。如果需要用 Java 开发图形桌面应用程序,则 Jigloo 是一个非常不错的选择。不过,那只是开始。 Jigloo 支持双向传递。那意味着您不但可以做出导致底层代码变化的可视化更改,而且还可以直接编辑代码并查看在 GUI 中引起的更改。如果配有现有的传统 GUI,这使 Jigloo 成为非常优秀的选择。它可以解释现有的应用程序并允许您进行可视化编辑。它还意味着您可以把 Jigloo 与其他 IDE(如 NetBeans 或 JBuilder)结合使用。 Jigloo 是与 Swing/SWT 兼容的。它支持任意一项技术所支持的许多布局选项。您不但可以使用任意一项技术构建新的 GUI,而且甚至可以使用 Jigloo 在 Swing 与 SWT 之间进行转换。Jigloo 也可以让您使用 SWT_AWT 桥在 SWT 应用程序内混用 Swing 组件。但是,在本教程中,我们将使用 SWT GUI 构建工作流应用程序。 安装 Jigloo Jigloo 是一个 Eclipse 插件,因此它非常易于安装。如果您已经使用了 Eclipse 很多年,那么有可能已经通过将其简单地解压缩到 Eclipse 安装目录中下载并安装了插件。最新版本的 Eclipse 甚至使此过程变得更加简单。您需要做的全部操作就是使用 Eclipse 的 Update Manager 功能。要访问此功能,请选择 Help > Software Updates > Find and Install。此操作将打开 Install/Update 对话框。您将需要选择 “Search for new features to install” 选项。然后只需单击 Next,您将转到 Update Site 对话框。 如果已经安装了 Eclipse 插件,“Sites to include in search” 中可能已经列出了其他的站点。如果是这种情况,您需要确保已经取消选中此列表中的任何其他站点。单击 New Remote Site,将打开 New Update Site 对话框,如下所示。 图 1. New Update Site ![]() 在这里,重要的是在 URL 字段中输入 http://cloudgarden1.com/update-site。您可以在 Name 字段中实际使用任何所需内容,但是您可能将需要使用诸如 Jigloo Update Site 之类的描述性内容。单击 OK,此操作将把您带回到 Update Site 对话框,但是现在您刚输入的更新站点应当会显示在要包括的站点列表中。单击 Finish。此操作将打开 Search Results 对话框。在 Search Results 对话框中,选择 Jigloo 并单击 Next。此操作将打开 Feature License。 如前所述,Jigloo 可以免费使用,但仅限于非商业使用。否则,您必须从 CloudGarden 获得一个专业许可证。当您阅读完许可证后,选择 “I accept the terms in the license agreement” 接受许可证。然后只需单击 Next。此操作将为您打开 Installation details 对话框。单击 Finish。此操作将打开 Feature Verification 对话框。您只需单击 Install 或 Install All。此操作将启动安装。Eclipse 将从 CloudGarden 下载并安装插件。完成后,您可能需要重新启动 Eclipse 才能完成安装过程。 祝贺您!您已经安装了 Jigloo。像大多数 Eclipse 插件一样,这是一次很轻松的体验。您现在已经准备好开始使用 Jigloo。首先需要进行一些配置。 设置 Jigloo 使用 Eclipse 创建一个新 Java 项目。选择 File > New > Project,然后选择 Java Project。单击 Next 将为您打开 New Project 对话框。 在本教程中,项目被称为 “工作流”。您可以根据需要使用任意名称。在您为其命名后,请单击 Finish。 如前所述,您可以使用 Jigloo 构建 Swing 或 SWT GUI。在本教程中,我们将构建一个 SWT GUI。这将要求进行一些额外配置。您需要把 SWT JAR 添加到项目的类路径中。为此,请选择项目,然后从主菜单中选择 File > Properties。这将打开 Project Properties 屏幕。在左侧导航区中选择 Java Build Path,此操作将打开 Java Build Path 对话框。 单击 Libraries 选项卡,然后单击 Add External JARs 按钮。此操作将打开一个文件资源管理器对话框。您需要导航到 $ECLIPSE_HOME/plug-ins,其中 $ECLIPSE_HOME 是 Eclipse 的安装位置,如下所示: 图 2. Eclipse 插件目录 ![]() 您需要查找 org.eclipse.swt.X.X.X.jar。X.X.X. 会因所使用的平台及实际的 Eclipse 版本而有所不同。单击 Open,然后单击 OK 返回到 Java Build Path 屏幕。 您已经创建了一个基本的 Java 项目,并已经把 SWT 库添加到其类路径中。现在,您已经准备好开始使用 Jigloo 来设计和开发工作流应用程序。 工作流应用程序 我们的示例是一个非常简单的工作流应用程序。我们的应用程序中将有两类用户:工人和经理。工人将使用该应用程序输入购买请求。他们将能够输入购买订单的相关信息。他们还将能够查看输入的所有订单的状态。每个购买订单可以有三种可能状态中的一种:待审批、批准、拒绝。经理们使用的应用程序略有不同。经理将能够看到状态为待审批的所有购买订单。然后经理可以批准或拒绝购买订单。 这将是一个简单的应用程序,因此我们不必担心用户登录和登出。相反,我们将简单表示系统中的用户列表;应用程序用户可以简单地选择希望使用的用户登录身份。在实际的工作流系统中,并发是个大问题。我们无需担心我们的应用程序会遇到这个问题,因为它将不会在多个系统上运行。实际的工作流系统还需要在共享存储库(通常为关系数据库)中保留工作流数据。我们将通过把数据简单保存在 XML 文件中使应用程序更简单。我们将使用 JAXB 打包和解包 XML 数据。 接下来,我们将开始为 GUI 编写实际的代码。 设置 GUI 既然我们有了系统的基本设计,是时候开始编写一些代码了。我们将把注意力集中在 GUI 上并使用 Jigloo。 设计工作流应用程序的 GUI 让我们开始设计工作流应用程序的 GUI。首先,让我们为工作流应用程序创建一个软件包。在 Package Explorer 窗格中右键单击项目并选择 New > Package。让我们把基本软件包称为 org.developerworks.workflow。单击 Finish。 现在,让我们在此软件包中创建应用程序。右键单击软件包并选择 New > Other。这将打开 “Select a wizard” 屏幕。可以在此屏幕的 GUI Forms 文件夹中找到所有 Jigloo 向导。打开该文件夹,然后打开 SWT 文件夹并选择 SWT Main Application 选项,如图 3 所示。 图 3. Select a wizard ![]() 单击 Next 打开 New SWTApp 向导。将主类命名为 WorkflowMain,如图 4 所示: 图 4. New SWTApp 向导 ![]() 您可以将其他内容保留原样并单击 Finish。 Jigloo 可视化设计器视图 现在 Eclipse 工作区应当类似图 5。 图 5. 显示 WorkflowMain 的 Eclipse ![]() 这里有很多有趣的内容需要注意。首先,我们的主窗口将显示 Jigloo 所使用的自定义视图。此视图是一个分块显示的屏幕。顶部是 Jigloo 可视化设计器。我们将在此对话框中完成很多工作,因为它允许我们通过使用拖放操作来设计 GUI。我们将更详细地介绍此视图。位于屏幕底部的是应用程序的 Java 代码,我们稍后将介绍。注意 Jigloo 是怎样已经为我们生成了大量代码。 在主窗口下,我们可以看到 GUI Properties 窗口。此视图将向我们显示在可视化设计器窗口中选定的任何可视化组件的详细属性。我们也将大量使用此视图。最后,请注意在 Package Explorer 视图中存在另一个软件包:com.cloudscape.resource。此软件包包含生成的类 SWTResourceManager。正如它的名称暗示的那样,此类将管理 SWT 资源,例如字体、颜色和图像。如果您曾使用过可视化设计工具(如 Jigloo),则可能已经看到过类似于这样的类。通常这些类包括关于不要修改类中任何内容的严厉警告,因为它们非常脆弱并且修改可能会造成异常,或者您所做的更改可能会被覆盖。那种情况不会出现在 SWTResourceManager 上。您可以修改它来更改它所管理的资源,如应用程序的默认字体,并且 Jigloo 将不会覆盖更改,它也不会导致任何中断。 让我们开始使用可视化设计器。如果您曾经使用过 Eclipse,则可能知道您可以通过简单地双击窗口的名称将任何窗口最大化。让我们双击窗口的名称把 WorkflowMain 窗口最大化,如图 6 所示。 图 6. 最大化的 WorkflowMain ![]() 这样做不但可以为可视化设计器提供更多真实的状态以供使用,而且使您可以更轻松地访问 GUI 属性(右下角)。它还在右上角显示 GUI 的层次结构表示。现在它还十分简单,但是随着我们向 GUI 中添加更多组件,我们将会看到这些组件添加到了层次结构中。它还为我们提供了预览或运行应用程序进行测试的有用控件。既然我们已经更加熟悉可视化设计器,那么让我们开始使用它来配置应用程序。 使用可视化设计器进行开发 如果您以前开发过 Swing 或 SWT 应用程序,则可能熟悉多种可用的布局管理器。布局管理器一直都是 Java GUI 应用程序的强大功能。对 GUI 布局时,布局管理器将允许应用各种算法。Java 的座右铭一直都是 “编写一次,可在任何位置运行”,并且这些布局算法都是与这个座右铭一致的。不管显示 GUI 的屏幕或窗口的几何尺寸如何,这些布局算法都允许 GUI 应用程序的布局正常运行。 这些布局算法惟一的弊端是它们向开发人员呈现一种学习曲线,尤其是那些曾经使用绝对定位元素的其他平台的开发人员。使用绝对定位,您只需将组件放置在屏幕上的所需位置,并且那就是它将在的位置。此类布局的问题在于当屏幕尺寸发生改变时它会被 “破坏”。 由于这种布局对于简单的示例应用程序十分有意义,因此我们将使用绝对布局来简化操作。为此,我们将使用 GUI 属性。单击 Layout 选项卡,并将 Layout 的值更改为 Absolute,如图 7 所示。 图 7. 配置布局 ![]() 接下来,让我们的主应用程序更大些。为此,只需拖住主窗口的右下角并将其伸展到所需的尺寸。 现在我们已经准备好开始向应用程序中添加可视化组件。我们必须能够在各种用户之间切换。我们可以提供一个登录屏幕,要求输入用户名和密码,但是让我们把事情变得简单一些。仅使用一个下拉式列表,您可以在其中选择用户的登录身份。 可视化设计器的顶部有一个工具栏,该工具栏中包含可以选择并添加到应用程序中的各种可视化组件。这是一个由选项卡组成的工具栏。我们需要添加控件,那么就选择 Controls 选项卡。每个控件都有一个直观的图标,但是您还可以把鼠标放在每个控件上来查看其名称。选择组合框控件并将其拖到我们的主窗口中。这将打开新控件的属性编辑器,如图 8 所示。 图 8. 组合框控件的属性编辑器 ![]() 我们已经将组件的名称更改为更直观的名称(userListCombo),并为其提供了一些默认的用户文本。单击 OK,您应当会在应用程序内看到它。 现在我们有一种方法可以选择和更改用户。当选择一个用户时,我们需要哪些操作发生?对于工人,他们应当能够看到到目前为止已经输入的所有购买订单和那些购买订单的状态。对于经理,他们需要看到那些待审批的订单,以便他们可以批准或拒绝订单。让我们创建一张表来显示购买订单。 要添加表,请从工具栏选项卡切换到 Containers 选项卡。查找类似表的图标并选择它。选择表后,您可以使用鼠标将其放置在所需的位置。让我们把它放在用户组合框列表下。当您为其选择位置后,系统将显示对话框来要求您为表命名。同样,让我们使用一个直观的名称,因此键入 purchaseOrderTable。单击 OK,您的表应当会显示在可视化设计器中。 您可以通过拖住表的右下角来扩展表使其变得更大。让我们更进一步地配置表。表的属性位于 Eclipse 窗口右下角的 GUI Properties 视图中。单击 Properties 选项卡并展开属性的 Expert 列表。通过单击复选框将 linesVisible 属性更改为 true,如图 9 所示。 图 9. 编辑表属性 ![]() 让我们把一些列添加到表中。为此,请使用工具栏上的 Containers 选项卡来选择 Add TableColumn to Table 图标,紧挨着我们先前选择的表图标。现在您可以将 TableColumn 简单地拖入 purchaseOrderTable,并且这将打开 TableColumn 属性编辑器。同样,使用一个描述性名称 poItemNameColumn,因为此列将 保存已经订购的条目的名称。文本将转到列的标签中,因此为其键入 Item 并单击 OK。 添加完此列后,可视化设计器应当类似图 10。 图 10. 添加完一列后的可视化设计器 ![]() 此时,您可能还看不到列的名称。如果是这样,请通过再次访问表的 Expert 属性来修正它。只需单击表,GUI Properties 将切换回表。打开 Expert 部分查找名为 headerVisible 的属性,如图 11 所示。 图 11. 编辑表属性 ![]() 该属性有一个复选框,因此可以将其设为 true 或 false。将其切换为 true,现在,您应当会在表中看到表头和第一列的条目标签。 让我们继续并且再添加四列:Price、Quantity、Status 和 ID。完成后,设计器应当类似图 12。 图 12. 添加到表中的所有列 ![]() 现在可以根据需要显示所有订单。我们需要有一种方法来批准或拒绝订单。让我们添加几个按钮来完成此操作,每个操作对应一个按钮。要添加按钮,请切换到工具栏上的 Controls 选项卡并且选择 Button 控件的图标。同样,您可以把按钮拖到所需的位置。让我们就把它放在表的下方。释放它后,系统将显示按钮的属性编辑器对话框。让我们把第一个按钮作为批准购买订单的按钮,因此我们将把它命名为 approveButton 并为其提供一个文本标签 Approve。单击 OK,可视化编辑器应当会显示新按钮。 类似地,我们将向应用程序中添加 Reject 按钮。完成后,可视化设计器应当类似图 13。 图 13. 添加的两个按钮 ![]() 您可能会注意到图中的两个按钮并没有完全对齐。很难用肉眼来对齐按钮,但是幸运的是,Jigloo 提供了一种轻松的方法可用于对齐组件。只需选中两个组件 —— 在这种情况下,为两个按钮 —— 并使用设计器左侧中的样式工具栏。我们将使用 Align tops of selected elements 按钮,如图 14 所示。 图 14. 对齐按钮 ![]() 按钮的布局看上去应当会很美观并且已对齐。Jigloo 可以轻松地让 GUI 看上去非常专业。 现在我们可以选择用户、查看购买订单,甚至批准或拒绝购买订单。我们需要做的全部操作就是添加一个购买订单。我们将创建一张表单以输入新购买订单的数据。表单将包括与模拟购买订单所需的各种数据相对应的若干个组件。让我们对这些组件分组。那将为我们提供一种方法按组来寻找所有组件,这将非常有用。例如,我们可能需要创建它,以便只有某些用户可以添加新购买订单。对于这类用户,我们可能需要使表单不可见。如果表单中的所有元素都被分组到一起,那么就更容易实现我们的需要。 要创建此分组,我们将向应用程序中添加 Composite 组件。切换到工具栏中的 Containers 选项卡并选择 Composite 图标,如图 15 所示: 图 15. 使用工具栏添加 Composite ![]() 将其释放到所需位置。在应用程序的右侧有一大块空白空间,因此那就是我们要释放它的位置。这将如预期地打开 Composite 的 Property Editor 对话框,如图 16 所示: 图 16. Composite 属性编辑器 ![]() 我们将 Composite 称为 itemForm,因为那就是它将要保存的内容。确保将其 Layout 更改为 Absolute。那是因为 Composite 可以有自己的 Layout 管理器。单击 OK 后,您应当会在可视化设计器中看到 Composite,如图 17 所示: 图 17. 可视化设计器中的 Composite ![]() 我们可以通过拖动 Composite 的右下角使其变得更大。让我们扩展它,以便我们有足够的空间来添加表单元素。 现在让我们来设计自己的表单。我们的表单将大部分是文本框和标签。我们将使用工具栏把这些元素添加到表单中。切换到 Controls 选项卡并选择标签图标。正如您所预料的那样,您可以将此图标释放到所需的任意位置。将其放在表单容器的左侧顶部。同样,这将打开普通属性编辑器,这次是 Label Control 的属性编辑器,如图 18 所示: 图 18. Label Control 属性编辑器 ![]() 您现在应当会在可视化设计器中看到标签。同样,您可以通过拖拽它的边角来调整标签的大小。 有了标签后,让我们在它旁边添加一个文本框以供用户在购买订单中输入条目的名称。从工具栏中选择 Text Control。现在,您知道了添加程序:将其释放到所需位置,然后它将打开自己的属性编辑器,如图 19 所示: 图 19. Text Control 属性编辑器 ![]() 我们给它提供了一个描述性名称 formItemText。我们还清除了 Text 值。这将用于某种默认值,但是我们实际上不需要该值。现在我们应当在可视化编辑器中看到文本框并且可以调整它的大小。 我们将继续并对另外两个字段重复执行此过程:Price 和 Quantity。每个字段都将获得一个标签和一个文本框。添加后,我们的可视化编辑器应当类似图 20。 图 20. 添加的 Price 和 Quantity 字段 ![]() 注意,我们提供了数量的默认值为 1。那些都是我们的购买订单中所需的基本数据元素。现在我们只需要一种方法来提交我们的表单以进行处理。让我们为此操作添加一个按钮,其方法和前面一样,从工具栏中选择 Button Control 等。这一次让我们使用另外一种技术。在 Composite 内简单地右键单击并选择 Add SWT Object > Button,如图 21 所示: 图 21. 使用上下文菜单添加按钮 ![]() 这将打开熟悉的属性编辑器对话框。把组件命名为 addButton 并为其提供文本 Add PO。单击 OK,我们将在设计器中看到新按钮,如图 22 所示: 图 22. 添加的 Add 按钮 ![]() 这种方法与使用工具栏的最大区别在于我们需要调整它相对于其他控件的位置和对齐情况。这将给我们提供一个机会来使用其他对齐控件。选择三个文本框和添加按钮(使用 shift 键选择多个选项),然后单击 Space selected elements evenly vertically,如图 23 所示: 图 23. 均匀地垂直分隔元素 ![]() 请大胆地尝试使用其他一些对齐控件。完成后,最终的 UI 应当类似图 24。 图 24. 最终的 UI 设计 ![]() 预览 GUI 我们现在有了一个很好的 GUI 界面。它还不能运行,因为我们还没有把任何数据与它绑定在一起。我们可以非常轻松地完成该操作,但是这是一个利用 Jigloo 的另一项优秀功能 Preview 的好机会。在右侧顶部窗口中单击 Preview。这将打开 GUI 的图形预览,如图 25 所示: 图 25. Workflow GUI 预览 ![]() 这就是最终应用程序的外观。您将会注意到的一大特点是这看似一个 Windows? 应用程序。那是因为我们使用了基于 Windows 的 SWT。如前所述,它将使用本机小部件。 创建应用程序的数据模型 如果熟悉典型的模型-视图-控制器(Model-View-Control,MVC)架构,那么您会认识到目前为止我们所做的操作是创建应用程序的视图。这是 Jigloo 所擅长的领域之一。在某种程度上,是时候创建模型了。那意味着编写一些 Java 代码来读取并对驱动系统的数据进行操作。Jigloo 同样擅长于此,但是它可以毫不费力地完成此操作。Jigloo 只是 Eclipse 的一个插件,因此在使用 Jigloo 时您总是能够使用 Eclipse 的全部功能。由于 Eclipse 非常适于编写 Java 代码和使用数据,因此 Jigloo 也非常适于执行这些操作。 创建模式 如前所述,我们将把数据存储在 XML 中,并使用 JAXB 从 XML 文件中读取和向 XML 文件中写入数据。那意味着我们需要创建一种模式。 清单 1. 工作流 XML 模式 <?xml version="1.0" encoding="UTF-8"?><schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="org:developerworks:workflow" xmlns:dw="org:developerworks:workflow"> <element name="workflow" type="dw:workflow"/> <complexType name="workflow"> <sequence> <element name="user" type="dw:user" minOccurs="0"maxOccurs="unbounded"/> <element name="po" type="dw:purchaseOrder"minOccurs="0" maxOccurs="unbounded"/> </sequence> </complexType> <complexType name="user"> <sequence> <element name="username" type="string"/> <element name="role" type="dw:role"/> </sequence> <attribute name="id" type="integer" use="required"/> </complexType> <simpleType name="role"> <restriction base="string"> <enumeration value="worker"/> <enumeration value="manager"/> </restriction> </simpleType> <complexType name="purchaseOrder"> <sequence> <element name="priority" type="dw:priority"/> <element name="dateRequested" type="date"/> <element name="dateNeeded" type="date" minOccurs="0"/> <element name="itemName" type="string"/> <element name="itemDescription" type="string" minOccurs="0"/> <element name="quantityRequested" type="integer"/> <element name="url" type="anyURI" minOccurs="0"/> <element name="price" type="decimal"/> <element name="status" type="dw:orderStatus"/> <element name="submittedBy" type="integer"/> <element name="processedBy" type="integer" minOccurs="0"/> </sequence> <attribute name="id" type="integer" use="required"/> </complexType> <simpleType name="priority"> <restriction base="string"> <enumeration value="normal"/> <enumeration value="high"/> </restriction> </simpleType> <simpleType name="orderStatus"> <restriction base="string"> <enumeration value="pending"/> <enumeration value="approved"/> <enumeration value="rejected"/> </restriction> </simpleType></schema> 我们可以使用 JAXB 创建绑定到与模式相对应的 XML 文件的 Java 类。如果使用的是 Java 6,JAXB 是内置的。如果使用的是 Java 5,则需要从 Sun 下载 JAXB。您需要使用命令行工具 xjc。对于本示例,您将简单地使用 xjc workflow.xsd。这将触发模式编译程序解析 workflow.xsd 并生成类。然后您只需将那些类复制到项目中。让我们也把模式复制到项目中并为此文件创建 XML 目录。然后,创建一个样例 XML 文件。 清单 2. 最初的 XML 数据 <?xml version="1.0" encoding="UTF-8"?> <dw:workflow xmlns:dw="org:developerworks:workflow"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="org:developerworks:workflowworkflow.xsd "> <user id="0"> <username>homer</username> <role>worker</role> </user> <user id="1"> <username>bart</username> <role>manager</role> </user> <po id="0"> <priority>normal</priority> <dateRequested>2001-01-01</dateRequested> <dateNeeded>2001-01-01</dateNeeded> <itemName>stapler</itemName> <itemDescription>A great stapler</itemDescription> <quantityRequested>2</quantityRequested> <url>http://www.thinkgeek.com/homeoffice/gear/61b7/</url> <price>21.99</price> <status>pending</status> <submittedBy>0</submittedBy> </po> </dw:workflow> 添加所有这些人工代码之后,我们的 Package Explorer 应当类似图 26(如果有必要,把 JAXB jar 添加到 Java Build Path 中)。 图 26. 添加了 JAXB 类和 XML 文件的 Package Explorer ![]() 数据访问 您可能注意到了资源管理器中的其他一些文件:WorkflowDao 和 XmlWorkflow。WorkflowDao 是定义处理数据所需操作的接口(参见清单 3)。 清单 3. WorkflowDao 接口 package org.developerworks.workflow;import java.util.List;public interface WorkflowDao { public List<User> getUsers(); public List<PurchaseOrder> getAllOrders(); public List<PurchaseOrder> getAllPendingOrders(); public List<PurchaseOrder> getOrdersForUser(int userId); public void saveOrder(PurchaseOrder order); public void setOrderStatus(int orderId, OrderStatus status);}我们使用的是典型的数据访问对象(Data Access Object)模式。我们将简单地定义接口并编写该接口的应用程序代码。在此应用程序中,我们将使用基于 XML JAXB 的实现,但是通过使用这种设计,我们可以轻松地切换到其他实现,例如基于数据库的实现。我们的接口实现是 XmlWorkflow。 清单 4. XmlWorkflow 接口实现 package org.developerworks.workflow; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; public class XmlWorkflow implements WorkflowDao { private static final String DATA_FILE = "data.xml"; private static XmlWorkflow instance; private Workflow workflow; private XmlWorkflow() { try { JAXBContext ctx = this.getContext(); Unmarshaller unm = ctx.createUnmarshaller(); File dataFile = this.getDataFile(); InputStream inputStream; if (dataFile.exists() && dataFile.length() > 0){ inputStream = new FileInputStream(dataFile); } else { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("xml/"+DATA_FILE); } JAXBElement element = (JAXBElement) unm.unmarshal(inputStream); this.workflow = (Workflow) element.getValue(); } catch (JAXBException e) { e.printStackTrace(); throw new RuntimeException("Failed to read data file",e); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Could not open data file", e); } } public static XmlWorkflow getInstance(){ if (instance == null){ instance = new XmlWorkflow(); } return instance; } public List<PurchaseOrder> getAllOrders() { return this.workflow.getPo(); } public List<PurchaseOrder> getAllPendingOrders() { List<PurchaseOrder> allOrders = this.getAllOrders(); List<PurchaseOrder> pending = new ArrayList<PurchaseOrder>(); for (PurchaseOrder order : allOrders){ if (order.getStatus().equals(OrderStatus.PENDING)){ pending.add(order); } } return pending; } public List<PurchaseOrder> getOrdersForUser(int userId) { List<PurchaseOrder> allOrders = this.getAllOrders(); List<PurchaseOrder> userOrders = new ArrayList<PurchaseOrder>(); for (PurchaseOrder order : allOrders){ if (order.getSubmittedBy().intValue() == userId){ userOrders.add(order); } } return userOrders; } public List<User> getUsers() { return this.workflow.getUser(); } public void saveOrder(PurchaseOrder order) { int index = 0; for (PurchaseOrder po : this.workflow.getPo()){ if (po.getId().intValue() == order.getId().intValue()){ this.workflow.getPo().set(index, order); this.saveData(); return; } index++; } // add new order order.setId(new BigInteger(Integer.toString(this.workflow.getPo().size()))); this.workflow.getPo().add(order); this.saveData(); } public void setOrderStatus(int orderId, OrderStatus status) { for (PurchaseOrder po : this.workflow.getPo()){ if (po.getId().intValue() == orderId){ po.setStatus(status); this.saveData(); return; } } } private void saveData(){ File dataFile = this.getDataFile(); try { JAXBContext ctx = this.getContext(); Marshaller marshaller = ctx.createMarshaller(); FileOutputStream stream = new FileOutputStream(dataFile); marshaller.marshal(this.workflow, stream); } catch (JAXBException e) { e.printStackTrace(); throw new RuntimeException("Exception serializing data file",e); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Exception opening data file"); } } private File getDataFile() { String tempDir = System.getProperty("java.io.tmpdir"); File dataFile = new File(tempDir + File.separatorChar + DATA_FILE); return dataFile; } private JAXBContext getContext() throws JAXBException { JAXBContext ctx = JAXBContext.newInstance("org.developerworks.workflow"); return ctx; } public static void main(String[] args){ XmlWorkflow dao = XmlWorkflow.getInstance(); List<User> users = dao.getUsers(); assert(users.size() == 2); for (User user : users){ System.out.println("User: " + user.getUsername() + " ID:" + user.getId()); } List<PurchaseOrder> orders = dao.getAllOrders(); assert(orders.size() == 1); for (PurchaseOrder order : orders){ System.out.println("Order:" + order.getItemName() + "ID:" + order.getId() + " Status:" + order.getStatus()); } PurchaseOrder order = orders.get(0); order.setStatus(OrderStatus.APPROVED); order.setProcessedBy(new BigInteger("1")); dao.saveOrder(order); } } |
联系客服