Grails的其他特性包括对Ajax、验证、单元测试和功能测试的内置支持。它使用免费的开源CanooWebTest项目来实现Web应用程序的功能测试。Grails还提供与QuartzScheduler的集成。
现在是时候安装Grails框架并且编写您的第一个应用程序了。
创建一个名为GRAILS_HOME的新环境变量,并将其值设为C:\groovy\grails-0.2.1\。接下来将GRAILS_HOME\bin添加到PATH环境变量。这样安装就完成了。通过在命令提示符界面中运行grails命令您可以检查安装是否成功。您应该获得此命令的使用信息。
既然您有了一个运行中的Grails安装,那么您已经为创建GrailsWeb应用程序做好了准备。
多年来我一直计划开发一个可以帮助我管理衣服的应用程序——这个应用程序应该能够告诉我我最喜欢的T恤衫放在哪里、是否洗过、是否熨过,等等。总有一天我会靠销售这个应用程序挣上几百万,但是现在我将把它用作Grails例子。
第一步是创建一个Grails项目目录结构。在这一步我将在C:\groovy\grailsapps创建一个新目录,并在此级别打开一个命令提示符窗口。在此窗口中,执行命令grailscreate-app。要求您输入应用程序名称。输入ClothesMgt。Grails将显示它为您创建的全部目录和文件。图1显示了最后得到的命令结构。
图1:Grails项目目录结构
此命令将创建约800KB大小的文件和目录。这里的想法是此框架遵循已经建立的Web应用程序开发惯例,因此它创建的文件和目录在大多数Web应用程序中是有用的。虽然有些人可能不喜欢这种强制使用某种结构的想法,但是这种基于惯例的自动生成正是Grails的RAD特性的基础。
如果更仔细地看一下这些目录,您就会发现存在用于诸如控制器、视图、测试、配置文件和标签库之类东西的目录。您还会发现存在一些基本Javascrīpt和CSS文件。那么现在应用程序的基本结构已经有了。您只需做些填空,应用程序即可就绪。
请注意自动生成目录和文件的命令是可选的。您可以手动创建全部文件和目录。如果熟悉ApacheAnt,那么您甚至可以打开GRAILS_HOME目录中的\src\grails\build.xml文件,来仔细查看每个Grails命令的用途。
在此例中我将使用一个运行于localhost的名为Clothes_Grails的MySQL数据库。Grails内置一个HSQL数据库,这对测试简单的应用程序或仅试用Grails非常有用。如果您使用HSQLDB,那么无需执行以下几步。我将使用MySQL来证明您能够非常轻松地使用HSQL之外的数据库。
现在此文件的内容应该类似以下内容:
classApplicationDataSource{booleanpooling=trueStringdbCreate="create-drop"Stringurl="jdbc:mysql://localhost/Clothes_Grails"StringdriverClassName="com.mysql.jdbc.Driver"Stringusername="grails"Stringpassword="groovy"}现在让我们看一下如何使用此数据库和对象关系映射。
Grails的对象关系映射(GORM)功能在内部使用Hibernate3,但是您无需了解或更改任何Hibernate设置。Grails具有称为“域类”的东西,这些域类的对象被映射到数据库。您可以使用关系来链接域类,它们也提供用于CRUD(创建/读取/更新/删除)操作的功能非常强大的动态方法。
在此例中,我们将创建三个域类,其名称分别是Shirt、Trouser和Cabinet。要创建域类,只需运行命令grailscreate-domain-class。请记住在您的项目目录(而不是它的上级目录)内运行此命令。这是一个常见错误,虽然我已经提醒了您,您还是会犯至少一次这样的错误。
您必须提供给create-domain-class命令的唯一输入是类的名称。运行此命令三次,将Shirt、Trouser和Cabinet作为三个域类的名称。Grails现在将在目录grails-app/domain/中创建这些域类。它们将仅具有两个属性id和version。我将为这些类添加属性,以便使它们更能代表衬衫、裤子和衣橱。
清单1:Cabinet.groovy
classCabinet{LongidLongversionStringnameStringlocationdefrelatesToMany=[shirts:Shirt,trousers:Trouser]Setshirts=newHashSet()Settrousers=newHashSet()StringtoString(){"${this.class.name}:$id"}booleanequals(other){if(other.is(this))returntrueif(!(otherinstanceofCabinet))returnfalseif(!id||!other.id||id!=other.id)returnfalsereturntrue}inthashCode(){inthashCode=0hashCode=29*(hashCode+(!id0:id^(id>>>32)))}}清单2:Trouser.groovy
classTrouser{LongidLongversionStringnameStringcolorCabinetcabinetdefbelongsTo=CabinetStringtoString(){"${this.class.name}:$id"}booleanequals(other){if(other.is(this))returntrueif(!(otherinstanceofTrouser))returnfalseif(!id||!other.id||id!=other.id)returnfalsereturntrue}inthashCode(){inthashCode=0hashCode=29*(hashCode+(!id0:id^(id>>>32)))}}清单3:Shirt.groovy
接下来我们将讨论Grails应用程序的控制器和视图部分。
清单4:ShirtController.groovy摘录
classShirtController{defindex={redirect(action:list,params:params)}deflist={[shirtList:Shirt.list(params)]}defshow={[shirt:Shirt.get(params.id)]}defdelete={defshirt=Shirt.get(params.id)if(shirt){shirt.delete()flash.message="Shirt${params.id}deleted."redirect(action:list)}else{flash.message="Shirtnotfoundwithid${params.id}"redirect(action:list)}}//...}在此例中,ShirtController中的list闭包将处理URI是/shirt/list的请求,等等。您可在控制器中使用您习惯在JavaWeb应用程序中使用的东西,例如请求、会话和servletContext。
请注意:闭包也将值作为显式返回语句返回,或者作为闭包体中的最后一个语句的值返回。不要因为Grails生成的代码中没有return而困惑。
一旦控制器完成了对请求的处理,它必须委托给合适的视图。Grails使用惯例机制实现此操作。因此ShirtController中的list闭包将委托给视图/grails-app/views/shirt/list.gsp或/grails-app/views/shirt/list.jsp。尽管您在使用Grails,全部视图可以是JSP文件而不是GSP。我几乎没有编写任何代码,但是我已经准备好了一个Web应用程序。
让我们尝试部署和运行我们的应用程序。
图2:Grails应用程序
通过此应用程序,能够获得Shirt、Trouser和Cabinet的全部CRUD功能。可以显示衣橱的全部数据、向衣橱添加新衬衫和裤子、编辑它们的值和删除记录——实现这些操作都无需编写任何业务逻辑、视图或数据访问代码。仅在几分钟内您就在JavaEE服务器上部署好了一个合适的Web应用程序。很酷吧?!
让我们更进一步来定制Grails。
我现在将把新功能和页面添加到Web应用程序,同时重用已经存在的域类。shirt/list和trouser/list会分别显示衬衫和裤子的清单,现在让我们添加一个新的显示,来同时显示衬衫和裤子的清单。要创建一个新的显示,您需要一个新的控制器和视图。
清单5:DisplayController.groovy
既然控制器能够处理请求、获取清单并转发到视图,我需要创建相应视图。
当创建控制器时,Grails还在grails-app\views目录创建了一个新的显示目录,并将以下映射添加到web.xml文件中。
图3:一个显示Trousers的默认视图
因为我希望创建一个独立于域类的视图,所以让我们手动创建视图文件。在目录grails-app\views\display\中,创建一个名为list.gsp的文件,如清单6所示。
清单6:list.gsp
图4:新创建的列出衬衫和裤子清单的视图
现在让我们快速讨论一下Grails服务。
让我们来看一个使用服务的例子。首先,使用create-service命令创建新服务。运行此命令并命名服务Order。Grails将创建两个文件——grails-app/services/OrderService.groovy和grails-tests/OrderTests.groovy。
现在编辑OrderService.groovy,如清单7所示。当引入新的orderGoods()方法时会自动生成serviceMethod()。
清单7:OrderService.groovy
classOrderService{booleantransactional=truedefserviceMethod(){//TODO}deforderGoods(){return"OrderPlaced-Newshirtsandtrousers\willbesentshortly."}}现在编辑DisplayController,如清单8所示。引入使用OrderService的重排闭包。请注意服务将由Groovy注入。
清单8:DisplayController.groovy
这些是可用的方法和属性,无需编写。这些动态方法涵盖了大多数Web应用程序开发中会碰到的常见情况。对于域类来说,存在诸如find()、findAll()、list()、executeQuery()、save()和delete()之类的动态方法。控制器具有诸如session、request和response之类的动态属性,以及诸如chain()、render()和redirect()之类的方法。要真正利用Grails的强大功能,您需要了解所有这些动态方法和属性的功能。
Grails的一个重要特性是能够在开发过程中进行了更改时自动重载文件。因此只需编辑和保存gsp文件,就会自动重载新文件。然而这里创建的类似OrderService的事务服务不会被重载。您会在服务器控制台看到以下消息"[groovy]Cannotreloadclass[classOrderService]reloadingoftransactionalserviceclassesisnotcurrentlypossible.Setclasstonon-transactionalfirst."。
在本文中,我介绍了Grails框架的基本特性,并使用Grails创建了一个应用程序。Groovy和Grails最大的好处是一切都运行在优秀的旧Java和JavaEE上——因此您能够使用Groovy和Grails的RAD特性快速开发应用程序,然后将应用程序部署到可靠的JavaEE服务器上。考虑到关于Ruby和Rails的宣传噪音,显然需要一个Java备选方案。Groovy和Grails看起来非常适合这个角色。