构建您的第一个Grails应用程序【IT168技术文档】请允许我借助另一种开源Web开发框架——RubyonRails——来介绍Grails。Rails一发布就迷住了开发人员。Rails的脚手架功能使您可以用以前所需时间的一小部分完成一个新的项目。支持Rails的约定优于配置(conventionoverconfiguration)思想意味着,应用程序可以根据常识性的命名模式自动进行组装(auto-wire),而不必借助繁杂的、容易出错的XML配置文件。Ruby的元编程功能使对象可以神奇地在运行时继承所需的方法和字段,而不会扰乱源代码。
Rails配得上它所受到的赞美和推崇(现在仍然如此),但是它使Java开发人员面临困难的选择。您会因为一个新平台的承诺而放弃自己熟悉的Java平台吗?如何处理已有的Java代码、已有的生产服务器和经验丰富的Java开发人员?
关于本系列
Grails是一种新型Web开发框架,它将常见的Spring和Hibernate等Java技术与当前流行的约定优于配置等实践相结合。Grails是用Groovy编写的,它可以提供与遗留Java代码的无缝集成,同时还可以加入脚本编制语言的灵活性和动态性。学习完Grails之后,您将彻底改变看待Web开发的方式。
Grails为您提供Rails风格的开发体验,同时以可靠的Java技术作为坚强后盾。但是Grails不仅仅是Rails通往Java平台的简单入口。Grails吸取了Rails的经验,并将它们与现代Java开发的意识相结合。可以认为Grails是受Rails启发,而不是由Rails转化而来。
作为Grails入门系列的开篇,本文介绍Grails框架,展示它的安装方法,遍览如何构建第一个Grails应用程序:介绍本系列后续文章的内容。
Groovy的威力
就像Rails与Ruby编程语言联系非常紧密一样,Grails也离不开Groovy(请参阅参考资料)。Groovy是一种动态语言,它在JVM上运行,并且与Java语言无缝集成。如果阅读了developerWorks上的大型实战Groovy系列,那么您已经了解了这种语言的威力。如果没有,也不必担心—在学习Grails的过程中,您将了解到很多关于Groovy的知识。Groovy应该不难学,因为它是特意为Java开发人员而设计的。
例如,Groovy可以大大减少Java代码的数量。在Groovy中,不再需要为字段编写getter和setter方法,因为Groovy会自动提供它们。不再需要编写forIteratori=list.iterator()来循环遍历一系列的项;list.each可以做相同的事情,而且看上去更简洁,表达更清晰。简言之,Groovy就是21世纪的Java语言。
如果Java开发人员只有重新编写整个应用程序才能利用Groovy,那么Groovy对他们就没有多大的吸引力了。令人高兴的是,Groovy可以无缝地与已有的代码库集成。Groovy不会替代Java语言—它只是提供了增强。您可以很快地掌握Groovy,因为说到底,Groovy代码就是Java代码。这两种语言是如此兼容,甚至可以将一个.java文件重命名为一个.groovy文件—例如,将Person.java改为Person.groovy—从而得到一个有效的(可执行的)Groovy文件(虽然这个Groovy文件并没有用到Groovy提供的任何语法)。
Groovy与Java语言的深度兼容意味着Grails不需要重新创造内部使用的关键技术。相反,您可以以Groovy的方式查看熟悉的Java库。Groovy封装了JUnitTestCase并以GroovyTestCase形式提供。Grails通过GANT对Ant构建进行了调整,GANT是Ant的一个纯Groovy实现。Grails将Hibernate包装在一个小小的Groovyfacade中,并称之为GORM—GrailsObject/RelationalMapper。Grails使您在利用已有的Java经验的同时,还可以利用最新的Web开发实践,以上只是其中的三个例子。
不过,要想全面地鉴赏Grails,还需要亲身体验一下。现在,让我们来安装Grails,并创建第一个Web应用程序。
安装Grails
运行Grails应用程序所需的一切都在一个ZIP文件中。所有的依赖库—例如Groovy、Spring和Hibernate—都已经在那里,随时可以使用。要安装Grails:
1.从Grails站点下载并解压grails.zip。
2.创建一个GRAILS_HOME环境变量。
3.将$GRAILS_HOME/bin添加到PATH中。
您的确需要安装一个JDK(Grails是不错,但是还没有好到那种程度)。Grails1.0可在Java1.4、1.5和1.6上运行。如果不知道已经安装了哪个版本,可以在命令行提示符下输入java-version。必要时,下载并安装一个与Grails兼容的JDK(见参考资料)。
完成安装步骤后,输入grails-version以进行检查。如果看到以下友好信息,则说明一切都得到正确配置:
?
WelcometoGrails1.0-http://grails.org/LicensedunderApacheStandardLicense2.0Grailshomeissetto:/opt/grails
附带的Web服务器和数据库
有趣的是,不需要单独安装Web服务器就可以运行Grails应用程序。Grails内置了Jettyservlet容器。只需输入grailsrun-app,就可以使应用程序在Jetty容器(见参考资料)中运行,而不必执行常见的部署过程。在已有的生产服务器上运行Grails应用程序也没有问题。通过输入grailswar创建一个标准文件,然后可以将其部署到Tomcat、JBoss、Geronimo、WebSphere?,或者任何其他遵从JavaEE2.4的servlet容器。
您也不需要单独安装数据库。Grails附带了HSQLDB(见参考资料),它是一个纯Java数据库。通过提供一个随时可用的数据库可以立即提高生产率。由于有了Hibernate和GORM,使用其他数据库(例如MySQL、PostgreSQL、OracleDatabase或DB2)也很简单。如果有一个JDBCdriverJAR再加上通常的连接设置,只需改变一下DataSource.groovy,就可以立即使用您自己的数据库。
编写第一个Grails应用程序
我经常旅行—一年至少40趟。我发现,日程表可以很好地告诉我何时需要达到某个地方,但是不能显示那个地方在哪里。而在线地图刚好相反:它们可以解决地点问题,但不能解决时间问题。所以,在本文和本系列接下来的两篇文章中,您将构建一个定制的Grails应用程序,在计划旅程时,这个应用程序既可以用于解决时间问题,又可以用于解决地点问题。
首先,在一个空白目录下,输入grailscreate-apptrip-planner。稍后,可以看到一个名为trip-planner的目录。同Maven、Rails和AppFuse一样,Grails会建立一个标准的目录结构。如果您觉得这个目录结构限制了您,并且只有精心设计自己的定制目录树才能使用一个框架,那么这样使用Grails不会有多大的乐趣。约定优于配置中的约定部分使您可以拥有任何Grails应用程序,并立即知道各个部分之间的联系。
进入trip-planner目录,并输入grailscreate-domain-classTrip。如果一切顺利,将得到两个新的文件:grails-app/domain/Trip.groovy和grails-app/test/integration/TripTests.groovy。在后面的文章中,我将谈到测试。目前,我们主要关注域类。一开始,域类看上去如清单1所示:
清单1.Grails生成的域类
classTrip{}
看上去没什么内容,对吗?接下来让我们来完善它。为Trip添加一些字段,如清单2所示:
清单2.添加字段后的Trip类
classTrip{??Stringname??Stringcity??DatestartDate??DateendDate??Stringpurpose??Stringnotes}
如前所述,这里不需要创建getter和setter方法:Groovy会动态地生成它们。Trip还有很多新的、有用的动态方法,这些方法的名称非常易用理解:
·Trip.save()将数据保存到HSQLDB数据库中的Trip表中。
·Trip.delete()从Trip表中删除数据。
·Trip.list()返回一个Trip列表。
·Trip.get()返回一个Trip。
所有这些方法都已经存在,您在需要的时候就可以使用它们。注意,Trip并没有扩展某个父类或者实现某个接口。由于Groovy的元编程功能,那些方法只是出现在适当类中的适当位置(只有grails-app/domain目录中的类才拥有这些与持久性相关的方法)。
构建控制器和视图
创建域类只是成功的一半。每个模型都还需要一个良好的控制器和一些视图(我假设您熟悉Model-View-Controller模式;请参阅参考资料)。输入grailsgenerate-allTrip,以构建一个grails-app/controllers/TripController.groovy类,并在grails-app/views/Trip中生成一组匹配的GroovyServerPage(GSP)。对于控制器中的每个list动作,都有一个相应的list.gsp文件。create动作则对应于一个create.gsp文件。从这里可以看出约定优于配置的优点:无需XML文件就可以匹配这些元素。每个域类根据名称与一个控制器配对。控制器中的每个动作也是根据名称与一个视图配对。如果您愿意,也可以绕开这种基于名称的配置,但是大多数时候只需遵循约定,应用程序自然就可以运行。
看看清单3所示的grails-app/controller/TripController.groovy:
清单3.TripController
classTripController{????...????deflist={????????if(!params.max)params.max=10????????[tripList:Trip.list(params)]????}????...}
Java开发人员首先会注意到的是,这么少的代码可以实现多少功能。以list动作为例。起重要作用的是最后一行。Grails将返回一个hashmap,其中只有一个名为tripList的元素。(Groovy方法的最后一行是一个隐式的return语句。如果您愿意,也可以手动地输入单词return)。tripList元素是Trip对象的一个ArrayList,Trip对象是通过Trip.list()方法从数据库中拉出的。通常该方法将返回表中的全部记录。它上面的一行代码表示“如果URL中提供了一个max参数,那么使用它来限制返回的Trip的数量。否则,将Trip的数量限制为10”。URLhttp://localhost:8080/trip-planner/trip/list将调用这个动作。例如,http://localhost:8080/trip-planner/trip/list?max=3显示3个trip,而不是通常的10个。如果有更多的trip要显示,Grails会自动创建上一页和下一页的分页链接。
那么,如何使用这个hashmap?看看grails-app/views/list.gsp,如清单4所示:
清单4.list.gsp
????????????${trip.id?.encodeAsHTML()}???? | ?? |
list.gsp主要是一些老式HTML加上少量GroovyTagLib。以g:为前缀的就是GroovyTag。在清单4中,g:each遍历tripListArrayList中的每个Trip,并构建一个格式良好的HTML表格。
对控制器的理解可以归结为三个R:return、redirect和render。有些动作利用隐式的return语句将数据返回到具有相同名称的GSP页面。有些动作进行重定向。例如,如果URL中未指定动作,则将调用index:
defindex={redirect(action:list,params:params)}
在此,TripController重定向到list动作,同时传递paramshashmap中的所有的参数(或QueryString)。
最后,save动作(见清单5)并没有相应的save.gsp页面。如果记录被成功地保存到数据库中,那么该动作会重定向到show动作页面。否则,它呈现create.gsp页面,以便显示错误,并让您重试。
清单5.save动作
defsave={??deftrip=newTrip(params)??if(!trip.hasErrors()&&trip.save()){????flash.message="Trip${trip.id}created"????redirect(action:show,id:trip.id)??}??else{????render(view:''create'',model:[trip:trip])??}}
在此,我们不详细讨论Grails是如何工作的,而是看看它的实际效果。
应用程序的实际效果
在命令行输入grailsrun-app。控制台在快速显示一批Log4j消息之后,将显示如下所示的消息:
Serverrunning.Browsetohttp://localhost:8080/trip-planner
如果端口8080上已经有一个服务器在运行,那么将显示一条核心转储信息:
Serverfailedtostart:java.net.BindException:Addressalreadyinuse
可以通过两种方法轻松更改Jetty所使用的端口。可以通过输入grails-Dserver.port=9090run-app临时进行更改。如果要使更改持久,可以从$GRAILS_HOME/scripts/Init.groovy中找出以serverPort开头的那一行,并更改值:
serverPort=System.getProperty(''server.port'')?????????????System.getProperty(''server.port'').toInteger():9090
使Grails在您选择的端口上运行之后,在Web浏览器中输入URL。应该可以看到一个欢迎屏幕,其中列出所有的控制器,如图1所示:
图1.Grails应用程序的欢迎屏幕
单击TripController链接。您有一个完整的CRUD(创建、读取、更新、删除)应用程序可以使用。
使用图2所示的页面创建新的trip:
图2.CreateTrip页面
使用图3所示的页面编辑trip:
图3.TripList页面
准备和运行这个应用程序要花多长时间?需要多少代码?下面就是答案:
按下Ctrl-C,关闭Grails。
输入grailsstats。
屏幕上将显示输出:
+----------------------+-------+-------+??|Name????????????????|Files|??LOC??|??+----------------------+-------+-------+??|Controllers??????????|????1|????66|??|DomainClasses??????|????1|????8|??|IntegrationTests????|????1|????4|??+----------------------+-------+-------+??|Totals??????????????|????3|????78|??+----------------------+-------+-------+
只需不到100行代码,就可以实现应用程序的所有功能。看起来还不错。不过,最后我还要再展示一个窍门。
生成控制器和视图是一项很好的学习体验,而磁盘上的物理文件则有助于说明各个部分是如何连接在一起的。不过在此需要做一件事:删除TripController类中的内容,并用下面的内容替代:
??classTripController{????defscaffold=Trip??}
这行代码告诉Grails像对待前一个控制器一样,在运行时在内存中动态地生成所有那些list、save和edit动作。仅仅3行代码就可以产生和66行代码一样的行为。
再次输入grailsrun-app。是的—所有数据都没有了。不必担心。按下Ctrl-C关闭Grails。这一次,输入grailsprodrun-app。现在处于生产模式下,这意味着在服务器重新启动之前,数据已被保存。通过一连串的单击进入TripController,保存一些记录。应用程序的行为应该没有什么不同。您已经知道,在浏览器中看到的一切,是由15行代码驱动的,可知Grails的威力有多大。
结束语
希望您对Grails的初次体验感到满意。小小一个包中,竟包含了令人惊讶的威力,而您只是看到冰山一角。这个框架的安装非常简单,只需解压一个文件。通过输入几行命令,就可以从头创建一个应用程序。希望这次简单的介绍能勾起您对Grails的更大兴趣。当然,本文也为您打好了一个基础,您可以扩展这个例子,尝试各种新的、有趣的方面。
在下个月的文章中,您将专门花一些时间来关注GORM。您将把日期保存到一个MySQL数据库中,进行某些数据验证,并设置一个一对多的关系。不必添加很多代码,就可以明显增强trip-planner应用程序的功能。
到那时,好好享受使用Groovy和Grails的乐趣吧。您对Web开发的看法将彻底改变。
4
|