开发人员:Java
Grails 开发简介使用 Oracle JDeveloper 作为 IDE 来构建简单的 Grails 应用程序。 作者:Harshad Oak 2008 年 3 月发布 紧随 Ruby on Rails (RoR) 潮流,大多数软件开发人员已开始考虑使用 ROR 或提供 ROR 功能的其他框架(如果尚未使用的话)。借着 Rails 的东风,Grails 框架也受到了广泛欢迎,但现在它已凭借自身实力发展成为一个功能强大的框架。Grails 使用 Groovy 编程语言;Groovy 语法与 Java 类似,但它还采用了其他语言中的某些概念。与 Java 一样,Groovy 生成字节码并在 Java 平台上运行,因此它不但具有 Java 的功能,而且更简单、更准确。本文介绍如何使用 Oracle JDeveloper 作为 IDE 来开发 Grails 应用程序。此外,您还在学习开发 Grails 应用程序的过程中大概了解 Groovy 的一些概念。 设置在所有 IDE 中,Oracle JDeveloper 安装属于最简单的安装过程之一。下载相关程序包,将其提取到所选目录中,此时即已准备就绪。如果在 Windows 上,请运行 jdeveloper.exe 文件;如果在 Linux 上,请运行 jdev 脚本。如果您是第一次使用 Oracle JDeveloper,建议您下载 Oracle JDeveloper Studio 版,此版本包含一个 JDK,有了它,即可在 Windows 和 Linux 上运行 Oracle JDeveloper,而不会出现任何问题。 Oracle JDeveloper 不提供现成的 Groovy 支持,但在 Oracle JDeveloper 10.1.3 中可使用 Groovy 扩展。要安装此扩展,请转至 Help -> Check for Updates。在 Update Centers 列表中,选择 Open Source and Partners Extensions。单击 Next。在显示的列表中,选择 Groovy 并单击 Finish。随后将下载 Groovy,并将其作为 Oracle JDeveloper 扩展进行安装。 Groovy 扩展向 Libraries 列表中添加 Groovy 库,此外还在 New Gallery 中添加创建 Groovy 脚本的选项。Groovy 扩展不能用于 Oracle JDeveloper 11g 技术预览版 3 及其以后的版本(请注意,安装 Groovy 扩展是可选操作。无需在计算机上安装 Groovy 扩展甚至无需安装 Groovy 即可使用 Grails。) 安装 Grails本文档编写期间,Grails 的最新版本为 1.0.1,Groovy 的最新版本为 1.5.4。安装过程非常简单。只需有 JDK 1.4 或更高版本,以及一个指向 JDK 安装的相应的 JAVA_HOME 环境变量即可。现在,下载最新的 Grails 二进制版本,并将内容提取到所选文件夹中。请注意,由于 Grails 包含 Groovy JAR 文件,因此无需单独下载和安装 Groovy。 接下来,创建指向该目录的 GRAILS_HOME 环境变量。将 %GRAILS_HOME%\bin 添加到 PATH 变量中。注意环境变量中不要存在拼写错误和空格。我似乎经常犯此类错误,然后花很长时间去研究为什么无法正常运行。如果在 Windows 计算机上错误地设置了 JAVA_HOME,将出现“System cannot find the path specified”(系统找不到指定路径)消息。此消息并未指明,问题与 JAVA_HOME 相关而与 .bat 文件或其他环境变量无关。 如果在 Windows 上,还可选择使用更容易的基于 .exe 的 Grails 安装程序。也可使用 Linux 基于 Debian 风格的 .deb 安装程序(请参见本文结尾的链接部分)。 现在 Grails 已设置完毕,我们来着手开发一个 Web 应用程序,此应用程序将自动完成会议的演讲者注册和论文提交过程。 使用 Oracle JDeveloper 进行开发的前几个步骤使用 Oracle JDeveloper 进行的所有开发都从创建应用程序和项目开始。通过 File -> New 创建一个新的应用程序。将该应用程序命名为 Grails。选择 No Template [All Technologies] 作为应用程序模板。将 confmgt 作为应用程序包前缀,并将 Conference 作为项目名称。 Grails 惯例和规则:Oracle JDeveloper 外部工具Grails 的核心思想是,只要开发人员遵循某些惯例,该框架就会自动为应用程序生成文件、目录和代码。它还将管理和运行应用程序,而不会事无巨细地都要求开发人员提供指令。如果开发人员遵循惯例,Grails 就会知道在什么情况下执行什么操作。 进行常规的 Java Web 开发时,在如何构建应用程序结构、将文件保留在什么位置、使用什么文件名等方面具有很大的灵活性。然而在使用 Grails 进行开发时,您需要遵循惯例(其中大部分惯例是可配置的)。习惯这种方式可能需要一个过程,但它确实可为您节省大量的时间和精力,因为所有乏味的、可重复的任务都由 Grails 处理。遵循严格的惯例还有一点很大的好处,即,一旦您了解了某个 Grails 应用程序的工作原理,就会很容易了解其他任何 Grails 应用程序的工作原理(而常规的 Java Web 开发并非始终如此)。 Grails 提供各种用于自动生成代码的命令。如果在命令提示符后键入 grails help,将会列出各种可用的 Grails 命令。可使用 External Tools(外部工具)功能将这些命令集成到 Oracle JDeveloper 中:
如下所示,在 Windows 计算机上输入 <grails 所在的目录路径>/bin/grails.bat 作为程序可执行文件,或者在 Linux 计算机上输入 <grails 所在的目录路径>/bin/grails 作为程序可执行文件。将 help 声明为参数。在这种特殊情况下,运行目录并不重要,因为无论在什么位置运行,help 命令都会生成所需的输出。然而,运行目录对于大多数其他 Grails 命令来说都是非常重要的。接下来,指定工具提示,并确定工具链接在 Oracle JDeveloper 中的显示位置。可让工具显示在上下文菜单以及工具栏的按钮中。 图 1 用于集成 Grails help 命令的 Oracle JDeveloper 外部工具 如下所示,还可选择在工具执行前保存所有文件或重新加载所有打开的文件。单击 Finish 创建新工具。现在应在所选位置显示新工具;默认情况下,新工具将显示在 Tools 菜单中。单击该工具,将会执行 Grails help 命令,并在日志窗口中列出各种 Grails 命令。在后面您将会创建更多工具。
图 2 外部工具设置 Oracle JDeveloper 尚未提供导入/导出外部工具的功能,因此您不能直接与团队中的其他人员共享您的工具。然而,可以将 Oracle JDeveloper 10.1.3 中的外部工具移到其他 Oracle JDeveloper 安装中,只需将相关的 XML 从 jdevhome/system/oracle.jdeveloper.10.1.3.40.66/tools.xml 文件复制到新计算机上的 tools.xml 文件即可。Oracle JDeveloper 11g 中没有 tools.xml 文件,所有首选项都在一个统一的文件 product-preferences.xml 中。此文件位于 Windows Vista 计算机的 C:\Users\username\AppData\Roaming\Oracle JDeveloper\system11.1.1.0.20.46.84\o.jdeveloper 中。 您也许想知道为什么应该使用外部工具,而不直接从命令提示符发出命令。使用外部工具的最大好处是可以在 Oracle JDeveloper 界面内执行所有操作。对于 Grails 初学者来说也更容易,因为可以将外部工具配置为自动提供不同的项目目录和路径作为命令参数。由于外部工具可自动保存文件和提供工具提示,从而进一步简化了操作。创建外部工具并与团队共享这些工具之后,可确保每个人都会从正确位置发出正确命令。最后一点也是相当重要的一点,使用外部工具要比在提示符后键入命令容易得多。 创建 Grails 应用程序Grails 提供了一个 create-app 命令用于创建基本的 Grails 应用程序结构。要执行此命令,请使用以下工具参数创建新的外部工具: Program executable:${env:var=GRAILS_HOME}\bin\grails.batArguments:create-app ${promptl:label=Name} Run directory:${workspace.dir} 在参数字段中,指定 Oracle JDeveloper 应该在您可以指定应用程序名称的位置给出提示。在工具创建向导中单击 Insert 按钮,浏览 Oracle JDeveloper 提供的各种其他宏。这些宏可帮助您提供动态值作为各种 Grails 命令的输入。由于您已经指定了工作区目录作为工具的运行目录,因此将在此目录中创建 create-app 命令生成的所有文件和文件夹。 使用外部工具执行 create-app 命令,并在提示符后指定应用程序名称 Conference。在日志中,您会看到 Grails 创建的很多目录和文件。 然而,Oracle JDeveloper 不会显示代码,因为在默认情况下,它会在 <项目目录>/src 目录中查找项目源代码。由于您已在工作区目录中创建了所有代码,因此需要在 Project Properties -> Project Content -> Java Content 中添加该目录。单击 Refresh 按钮,将显示 Application Navigator,如图 3 所示。 图 3 Application Navigator 现在,我们已经有了应用程序的基本结构。请查看各种目录和文件,以了解 Grails 组织 Web 应用程序的方式。请注意,Grails 创建的空目录未列在 Application Navigator 中。您将会看到,Grails 已创建了 views、taglibs、services 和 controllers 等目录,可有效强制开发人员基于功能组织代码。这一点对于团队项目来说非常有用,因为您可确保团队所有成员都遵循该惯例,而不是各行其事。 现在开始创建您的自定义应用程序,该应用程序提供演讲者注册、演讲者登录和论文提交功能。 Speaker 和 PaperGrails 提供了一个功能强大的 GORM(Grails 对象关系映射)形式的持久性机制,此机制基于 Hibernate 对象关系持久性。因此,开发人员可以创建域类并定义这些域类之间的关系。持久性由 GORM 实现。 域类位于大多数使用 Grails 开发的数据驱动 Web 应用程序的核心。域类为特殊类,不仅因为它们所包含的关系、规则和逻辑,而且因为 Grails 在运行时注入这些域类中的功能。我们将在应用程序中创建两个域类(Speaker 和 Paper)并查看其功能。在此示例中,域类的设计非常简单,且未标准化成更小的单元,但您可以很容易地将其标准化,方法是创建新的域类并设置这些域类之间的关系(就像在 Speaker 和 Paper 之间设置的关系一样)。 要创建域类,需要使用 Grails 命令 create-domain-class。您将在 Oracle JDeveloper 中创建新的外部工具,所用命令行与先前创建的工具相同。下面是唯一不同的几个地方: Program executable:${env:var=GRAILS_HOME}\bin\grails.batArguments:create-domain-class ${promptl:label=Name} Run directory:${project.dir} 运行 create-domain-class 工具。在提示符下输入 Speaker。将创建新文件 /grails-app/domain/Speaker.groovy。接下来,创建 Paper 域类。也会在域目录中创建 Paper.groovy 文件。现在您需要定义 Speaker 和 Paper 的属性以及二者的关系。 在 Oracle JDeveloper 中打开 Speaker.groovy。如果运行 Oracle JDeveloper 10.1.3 并安装了 Groovy 插件,将显示标记 .groovy 文件的图标。按如下所示编辑 Speaker.groovy 文件。 class Speaker { // Fields String userid String password String fname String lname String address1 String address2 String city String state String country String bio String company String speakingExp String email String phone String mobile //default values String status = "active" Date lastLoginDate = new Date() Date registerDate = new Date() // Optional - Can be null def optionals = [ "status", "address2", "company","mobile", "speakingExp"] // One speaker has many papers static hasMany = [papers:Paper] // Constraints used for validations static constraints = { userid(size:5..10,unique:true) password(size:5..10) fname(size:1..30) lname(size:1..30) email(email:true) phone(size:6..15) city(size:1..20) state(size:1..20) country(size:1..20) address1(size:5..200) bio(size:10..1000) } }在 Speaker 类中,您定义了希望演讲者提供的信息的属性、可以为空的字段、需要验证的属性的约束条件,以及 Speaker 和 Paper 类之间的关系。单词 optionals、hasMany 和 constraints 是 Grails 提供的特殊设置。在此示例中,由于 hasMany 设置,Grails 会将 java.util.Set 类型的属性注入 Speaker 中。 接下来,修改 Paper.groovy,如下所示。 class Paper { // Fields String title String description String comments String status = "active" Date lastModifiedDate = new Date() Date createdDate = new Date() Speaker speaker //Every paper belongs to a speaker def belongsTo = Speaker static constraints = { title(size:5..200) description(minSize:10, maxSize:2000) } def optionals = [ "comments", "status"] }belongsTo 属性通知 Grails 每张论文都属于某个演讲者。因此,如果删除该演讲者,将同时删除其论文。在 Paper 类中再次使用了 constraints 和 optionals。
现在已创建了域类,您将使用 Grails 的快速代码生成功能生成引用相应域类的各种代码文件,并为控制器、GUI 等生成代码。从本质上说,只需执行一个命令,即可为每个域类生成完整的创建/读取/更新/删除 (CRUD) 应用程序。您将为 generate-all 命令创建外部工具。此外部工具的参数如下: Program executable:${env:var=GRAILS_HOME}\bin\grails.batArguments:all ${promptl:label=Name} Run directory:${project.dir} 需要运行 generate-all 工具两次,每次提供一个域类作为参数。日志将显示 Grails 创建的所有文件和目录。现在可以针对 Speaker 和 Paper,试用这一具 CRUD 功能的 Grails 应用程序。我们先尝试运行此自动生成的应用程序,然后再对其进行自定义。只需运行 run-app 命令即可运行该应用程序。使用以下设置为 run-app 命令创建外部工具: Program executable:${env:var=GRAILS_HOME}\bin\grails.batArguments:run-app Run directory:${project.dir} Grails 附带一个内置的 Jetty 服务器,执行 run-app 命令时,将启动该服务器并在上面部署应用程序。虽然可在任何 Java EE 服务器上运行 Grails 应用程序,但开发期间在内置服务器上运行是最容易的一种方法。请在浏览器中访问 http://localhost:8080/Conference/,您会发现一个可以添加、编辑和删除演讲者和论文的 Web 应用程序。 数据源在 grails-app/conf/DataSource.groovy 文件中可找到数据源配置。Grails 附带一个可使用此文件进行配置的内置 HSQL 数据库。请注意,在默认情况下,dbCreate 属性的值为“create-drop”。因此每次重新启动服务器时,都会删除旧数据库并创建新数据库。如果要保留旧值,请将该属性值更改为“update”。 要使用 HSQL 之外的数据库,请将该数据库的 JDBC 驱动程序添加到 lib 目录中,然后将连接字符串和驱动程序类名称添加到 DataSource.groovy 配置文件中。例如,要使用 Oracle 数据库 10g 快捷版作为生产数据库,需将 Oracle JDBC 瘦驱动程序添加到 lib 目录,然后按如下所示编辑 DataSource.groovy 文件: production { dataSource { dbCreate = "update" url = "jdbc:oracle:thin:@localhost:1521:XE" driverClassName = "oracle.jdbc.OracleDriver" } }Grails 的创建者提供了特别常见的应用程序开发要求:必须在开发、测试和生产数据库之间进行切换,因此在 DataSource.groovy 文件中存在多个数据源配置。通过将参数(dev、test、prod)传递给 run-app 命令,可以更改用于运行应用程序的数据库。默认值为“development”或“dev”。 grails run-app grails dev run-app grails prod run-app grails test run-app现在您有一个应用程序,可供会议组织者输入演讲者数据和论文数据,但是很显然,该应用程序目前还无法联机发布并由演讲者直接使用。我们先来了解一下 Grails 的工作原理,然后再自定义该应用程序。 域、控制器和视图Grails 的运行围绕着域、控制器和视图。我们来分析 Grails 中的一个工作流,以了解 Grails 的工作原理。在 http://localhost:8080/Conference 显示的页面有一个指向 http://localhost:8080/Conference/speaker/ 的链接。单击此链接将转至位于 http://localhost:8080/Conference/speaker/list 的演讲者列表页面。本例中涉及的步骤如下:
自定义布局Grails 针对布局使用 SiteMesh。这些布局位于 grails-app/views/layouts 目录中。在我们前面查看的 list.gsp 视图中,您会注意到以下行 <meta name="layout" content="main"></meta>sch此行通知 Grails 为此特定视图使用 main.gsp 中声明的布局。因此,如果您要修改该页面的布局,则需要编辑 main.gsp 文件。我们将徽标由 Grails 徽标更改为已放入 web-app/images/ 目录中的 OTN 徽标。 <div class="logo"><img src="${createLinkTo(dir:'images',file:'otn_logo_small.gif')}" alt="OTN" /></div>通过修改 main.gsp 文件,可以很容易地添加标题和页脚、关联 CSS 文件,并对使用此布局的所有页面进行其他更改。请注意,也可以不使用 main.gsp 方法,而使用惯例处理 GSP 布局。因此,如果您创建名为 grails-app/views/layouts/speaker.gsp 的布局,它将应用于 SpeakerController 委托给的所有视图。还可以为单个操作创建布局,因此可在 grails-app/views/layouts/login/logout.gsp 中定义 LoginController 中注销操作的布局。 登录视图通过自动生成的应用程序,http://localhost:8080/Conference 页面提供了控制器列表。而您却需要一个演讲者可从中登录/注册到应用程序的页面。您的第一个页面要为已注册的演讲者提供一个登录表单。该页面还要为要注册的演讲者提供 Register 链接。基于此,我们来创建 grails-app/views/login/index.gsp 视图。Oracle JDeveloper 没有用于创建 GSP 文件的向导,因此需要从 New Gallery 中使用 General -> File 创建 GSP 文件。GSP 文件与 JSP 文件非常相似,并包含 HTML 和 GSP 标记。因此,可以将扩展名 .gsp 与 Tools -> Preferences -> File Types 中的文件类型 JSP Source 相关联,以启用 GSP 文件的语法突出显示功能。警告:不要对 JSP 文件使用 Reformat 工具,因为当 Oracle JDeveloper 尝试使用适用于 JSP 的规则重新格式化 JSP 文件时,GSP 代码将变成乱码。 现在编辑 Grails\Conference\grails-app\views\login\index.gsp(示例代码 zip 文件)中显示的 index.gsp。请注意 GSP 标记 if、 form 和 link 的用法。对于 Grails 生成的其他 gsp 文件,将通过 Grails 对 SiteMesh 的支持来管理布局。 注册演讲者演讲者必须先进行注册,然后才能登录到应用程序。为了实现此目的,您已经提供了指向 SpeakerController 的链接,以及在前面部分创建的 index.gsp 中的创建操作。Grails 已自动生成 SpeakerController 中的创建操作以及 create.gsp 视图。您需要修改它们以满足此要求。 首先修改 Grails\Conference\grails-app\views\speaker\create.gsp 文件(示例代码 zip 文件),删除已定义默认值且不希望用户输入数据的所有字段。因此,我们删除 lastLoginDate、status、optionals 和 registerDate 字段,并将 speakingExp 修改为文本区域,而不是文本字段。 在此 GSP 中,我们使用标记 hasErrors 显示在提交表单时可能发生的所有验证错误。此外,还要注意获取 Speaker 中个人简历字段的值以及将内容编码为 HTML 的表达式的用法,如 ${speaker?.bio?.encodeAsHTML()}。还要注意 ?. 操作符,它用于处理 Java 中引发的最常见异常 NullPointerException。使用 ?. 操作符时,如果该操作符前面的引用是空引用,则当前表达式的评估将停止,系统将返回空值。 请注意,虽然我们使用了基本文本框,但 Grails 提供了 richTextEditor 标记,可以获取易于使用的 HTML 编辑器。该标记使用富文本编辑器 FCKEditor。 LoginController要在提交用户名和口令表单(在上述 index.gsp 页面中创建)时执行实际的登录功能,需要创建一个新控制器 LoginController。首先创建 create-controller 命令的外部工具: Program executable:${env:var=GRAILS_HOME}\bin\grails.bat class LoginController { def index = { if (session?.speaker) { redirect(controller:"paper",action:"index") } } def login = { if (params.userid && params.password) { def speaker = Speaker.findByUseridAndStatus(params.userid, "active") String password = params.password if (speaker != null && speaker.password == password) { session.speaker = speaker speaker.lastLoginDate = new Date() speaker.save() flash.message = "Welcome ${speaker.fname} ${speaker.lname}" redirect(controller:"paper",action:"index") } else { flash.message = "Invalid Login." redirect(action:"index") } } else { flash.message = "Invalid Login." redirect(action:"index") } } def logout = { session.speaker = null flash.message = "You have been logged out" redirect(controller:"login",action:"index") } }在此控制器中,已定义了三个操作:
更新论文最后,与您在前面看到的 Speaker create.gsp 文件非常相似,您还必须修改其他 GSP 文件以删除不希望用户查看或编辑的字段。因此应删除 GSP 文件和 PaperController 更新操作中的 status、lastModifiedDate 和 createdDate 字段,添加 paper.lastModifiedDate = new Date(); 行以便在每次更改时存储更新的数据。在保存操作中,添加 paper.speaker = session?.speaker 行以便将正在创建的论文与当前登录的演讲者相关联。 用户验证由于我们不希望用户在未登录而只输入正确 URL 和参数的情况下就能访问数据,因此您需要在控制器操作中提供某种验证。使用 Grails 中的拦截器可以很容易地实现此目的。使用以下代码向 PaperController 中添加拦截器: def beforeInterceptor = [action:this.&checkUser] def checkUser() { if(!session.speaker) { flash.message = "Please login" redirect(controller:'login', action:'index') return false; } }此处使用的 before 拦截器在操作执行之前拦截处理,并调用 checkUser 方法。如果登录时设置到会话中的 speaker 对象在 LoginController 中不存在,则表明有人在未正确登录的情况下访问 PaperController 的操作,因此该方法返回“false”且不执行后续操作。 UrlMappings您希望应用程序流程以登录屏幕(可通过 http://localhost:8080/Conference/login/index.gsp 访问)开始。如果不想使用此 URL,而希望将更简单的 http://localhost:8080/Conference/ 映射到登录页面,请编辑 conf 目录中的 UrlMappings 文件: class UrlMappings { static mappings = { "/"(controller:"login") "/$controller/$action?/$id?" { constraints { // apply constraints here } } } }此处添加了 "/"(controller:"login") 行,用于将斜线 (/) 映射到登录控制器。默认情况下,登录控制器转至索引操作,您将获得所需的登录页面。 最后的修改针对 Speaker 和 Paper 生成的视图和控制器为两个域类都提供了 CRUD 功能。然而在实际情况中,您不希望任何新的演讲者看到所有其他演讲者的名单或删除演讲者,也不希望用户能够看到主键或者编辑状态常量、创建或修改日期,等等。因此,您需要打开视图文件,并确保要包含的内容符合您的期望和应用程序的需求。 打包和部署Grails 附带一个有用的 war 命令,可以将 Grails 应用程序打包到可在任何 Java EE 应用服务器上部署的标准 WAR 文件中。外部工具 war 将采用以下设置: Program executable:${env:var=GRAILS_HOME}\bin\grails.bat Arguments:war Run directory:${project.dir} 结论通过对 Grails 自动生成的代码进行某些更改,您现在拥有一个可以注册新演讲者并接受其论文的应用程序。您可以看到 Grails 域类如何通过强大的功能轻松获取和存储数据;如何修改控制器类中的操作以便基于某些附加流程逻辑提供自定义视图;最后,您还看到了 GSP 视图如何轻松生成丰富的用户界面。Grails 的当前版本为 1.0.1,其稳定性和强大功能预示着该框架将会被越来越多的人采用。虽然 Oracle JDeveloper 尚未附带针对 Groovy 和 Grails 的特殊功能,但利用 Oracle JDeveloper 的外部工具功能可以很容易地设置 Oracle JDeveloper 从而进行 Grails 开发。 Harshad Oak (harshad@rightrix.com) 是 Rightrix Solutions 的创始人,同时也是 Oracle 融合中间件的 Oracle ACE 总监。他是《Oracle JDeveloper 10g:Empowering J2EE Development》 (Apress,2004)和 《Pro Jakarta Commons》(Apress,2004)的作者,并与他人合著了 《Java 2 Enterprise Edition 1.4 Bible》(Wiley & Sons,2003)。 |
||||||||||||||||||||||||||||||||||