不知不觉间已经到了本系列的最后,在这一节里,我们将看到如何在Grails中使用Web服务,了解其与Spring和Hibernate相关的配置,以及关于脚手架和部署有关的内容。
Web服务如今已经成为潮流,不论你是否愿意,只要你还在开发应用,你就得去适应它。Grails支持:
- REST,针对Controller,通过URL Mapping,每个HTTP方法对应一个Action
- SOAP,通过plugin完成
- RSS/ATOM,通过Feed plugin
实现Restful的服务在Grails中简直就是小菜一碟,通过URL Mapping就可以将一个普通的Controller暴露为一个Restful服务:
static mappings = {
"/product/$id?"(resource:"product")
}
缺省Http方法和Action的对应关系:
- GET:show
- PUT:update
- POST:save
- DELETE:delete
如果你不满意,也可以自行调整:
"/product/$id"(controller:"product"){
action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]
}
但是该方法不会像前面一样自动进行JSON/XML的Marshalling,除非在映射中指明parseRequest参数:
"/product/$id"(controller:"product", parseRequest:true){
action = [GET:"show", PUT:"update", DELETE:"delete", POST:"save"]
}
客户端调用Restful服务的方法也不复杂:
- 使用Groovy HTTPBuilder:
import groovyx.net.http.* import static groovyx.net.http.ContentType.JSON def http = new HTTPBuilder("http://localhost:8080/amazon") http.request(Method.GET, JSON) { url.path = '/book/list' response.success = {resp, json -> json.books.each { book -> println book.title } } } - 从浏览器发起非GET/POST方法:
<g:form controller="book" method="DELETE">
关于XML Marshalling:
- 读:可以参见这里
- 写,步骤1:传入xml
<?xml version="1.0" encoding="ISO-8859-1"?> <product> <name>MacBook</name> <vendor id="12"> <name>Apple</name> </vender> </product> - 写,步骤2:数据绑定
def p = new Product(params['product']) p.save()
实现SOAP服务是通过XFire Plugin实现的,它可以把Service暴露为Web服务:
class BookService {
static expose=['xfire']
Book[] getBooks(){
Book.list() as Book[]
}
}
如果想知道它的wsdl,可以访问:http://127.0.0.1:8080/your_grails_app/services/book?wsdl
同样,对于RSS/Atom的支持,Grails也是通过插件:Feed Plugin。它底层使用的是ROME库:
def feed = {
render(feedType:"rss", feedVersion:"2.0") {
title = "My test feed"
link = "http://your.test.server/yourController/feed"
Article.list().each() {
entry(it.title) {
link = "http://your.test.server/article/${it.id}"
it.content // return the content
}
}
}
}
Spring应用中都会使用到ApplicationContext,Grails是一个Spring应用,当然也不例外,其中的ApplicationContext构造过程如下:
- 从web-app/WEB-INF/applicationContext.xml构造父ApplicationContext,它会构造出GrailsApplication和GrailsPluginManager。
- Grails利用GrailsApplication分析出惯例,然后构造子ApplicationContext,它作为Web应用的根ApplicationContext。
Grails的配置很多时候都发生在运行时,每个插件会给ApplicationContext注册一些Bean,关于这请查阅相关文档。当然,如果你对其最终的ApplicationContext感兴趣,不妨试试Spy插件,本站对它已有介绍。
Grails并没有堵塞你自行定义Spring配置文件的道路,如果你想补充进行额外配置:
- 方法1:grails-app/conf/spring/resources.xml
- 方法2:grails-app/conf/spring/resources.groovy。这除了享受到groovy语法,还可以结合“环境”。
在配置文件中可以引用Grails中的对象,如dataSource、sessionFactory。Grails还引入了Bean DSL,例子:
import grails.util.*
beans = {
switch(GrailsUtil.environment) {
case "production":
myBean(my.company.MyBeanImpl) {
bookService = ref("bookService")
}
break
case "development":
myBean(my.company.mock.MockImpl) {
bookService = ref("bookService")
}
break
}
}
这种DSL一般规则:
- 方法名为bean名
- 第一个参数为Bean的类名
- 闭包定义bean相关的属性
- 第一个参数和闭包之间是构造函数的参数
- 通过bean名即可引用Bean
这部分功能,即Bean Builder,亦可单独使用,在参考文档中给出了在Spring MVC应用中使用它的例子,这里就不重点阐述了。
Bean DSL的常见例子:
- 构造函数
exampleBean(MyExampleBean, "firstArgument", 2) { someProperty = [1,2,3] } - 工厂方法
exampleBean(MyExampleBean) { bean -> bean.factoryMethod = "getInstance" bean.singleton = false someProperty = [1,2,3] } - Factory Bean
repositoryService(org.jbpm.api.RepositoryService){ bean-> bean.factoryBean = "processEngine" bean.factoryMethod = "getRepositoryService" } - 动态创建
"${beanName}Bean"(MyExampleBean) { someProperty = [1,2,3] } - 匿名(内部)Bean
marge(Person.class) { name = "marge" husband = { Person p -> name = "homer" age = 45 props = [overweight:true, height:"1.8m"] } children = [bart, lisa] } - 抽象Bean和父Bean定义
abstractBean(KnightOfTheRoundTable) { bean -> bean.'abstract' = true leader = "Lancelot" } quest(HolyGrailQuest) knights("Camelot") { bean -> bean.parent = abstractBean <-注意 quest = quest }
Spring的配置文件中可以使用XML的名字空间,在Bean DSL中也可以:
- 声明:xmlns context:"http://www.springframework.org/schema/context"
- 使用(component-scan是context名字空间中定义的标签):context.'component-scan'( 'base-package' :"my.company.domain" )
名字空间使用例子
xmlns aop:"http://www.springframework.org/schema/aop"
aop {
config("proxy-target-class":true) {
aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) {
after method:"onBirthday",
pointcut: "execution(void ..Person.birthday()) and this(person)"
}
}
}
此外,我们还可以在Spring配置文件中使用Grails Config文件中定义的变量,配置文件中定义的脚本变量都可作为grails-app/conf/spring/resources.xml中的占位符
- Config
database.driver="com.mysql.jdbc.Driver" database.dbname="mysql:mydb" - Resources.xml
<bean id="dataSource" class="org.springframework.jdbc.datasource .DriverManagerDataSource"> <property name="driverClassName"> <value>${database.driver}</value> </property> <property name="url"> <value>jdbc:${database.dbname}</value> </property> </bean>
如果你想覆盖缺省的配置,可以按下列格式:
[bean name].[property name] = [value]
Grails中对于Hibernate的处理与Spring的非常类似:
- 配置文件:grails-app/conf/hibernate/hibernate.cfg.xml
- 映射文件:grails-app/conf/hibernate
Grails中还考虑到了你的现有代码资产,重用现有Domain Class(Java类):
- 步骤1:配置文件和映射文件复制到相应目录
- 步骤2:复制Java代码 => src/java
Grails也支持使用注解创建Domain Class:
- 在src/java中创建java类,在类中使用注解
- 注册
<!DOCTYPE hibernate-configuration SYSTEM "http://hibernate.sourceforge.net/ hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <mapping package="com.books" /> <mapping class="com.books.Book" /> </session-factory> </hibernate-configuration>
如果你想改变配置文件的位置,在DataSource.groovy中:
hibernate {
config.location = "file:/path/to/my/hibernate.cfg.xml"
}
配置文件也可有多个:config.location = ["file:/path/to/one/hibernate.cfg.xml","file:/path/to/two/hibernate.cfg.xml"]
Grails的脚手架提供的功能:
- 需要的view
- Controller中与CRUD对应的Action
激活脚手架很简单,设置Controller的scaffold属性:
- Controller名字和Domain Class名字相同时:def scaffold= true
- Controller名字和Domain Class名字不同时:def scaffold= domain class name
至于其他需要知道的知识:
- 可以添加其它Action
- 覆盖脚手架产生的Action就是定义与之名字相同的Action。脚手架产生的Action有list、show、edit、delete、create、save、update
- Domain Class的约束会对View产生影响,属性在约束中出现的顺序,也是它们在View中出现的顺序。
脚手架相关的命令:
- generate-controller DomainClass名
- generate-views DomainClass名
- generate-all DomainClass名
如:grails generate-all com.bookstore.Book,建议使用包名。
如果想自定义模板:
- 先安装模板:grails install-templates
- 再修改模板:src/templates
跟部署相关的命令有:
- grails run-app,它有两个环境变量:disable.auto.recompile,关闭检查;recompile.frequency,设置检查的秒数,缺省为3
- grails run-war,运行的是war,会关闭reload
grails war比较特殊,有必要单独说明:
- 改变缺省war的目录:方法1:grails war path;方法2:修改grails-app/conf/BuildConfig.groovy中的grails.war.destFile = "foobar-prod.war"
- 缺省包含的lib:grails必需的lib+工程lib+插件lib
- 如果你想自行定义相应的lib,那么修改BuildConfig.groovy,相关的属性:grails.war.dependencies、grails.war.copyToWebApp、grails.war.resources
后记:终于完工了!自此,你应该对Grails有了一个大致的了解,还有更多的秘密等待着你的探索。再次重申,本系列只是一个快速概览,并不能代替参考文档,顶多相当于一个敲门砖!最后,预祝大家虎年愉快,生龙活虎!
- Grails 1.2参考文档速读(1):第1、2章
- Grails 1.2参考文档速读(2):配置基础和环境
- Grails 1.2参考文档速读(3):日志配置
- Grails 1.2参考文档速读(4):第3章剩余内容
- Grails 1.2参考文档速读(5):第4章
- Grails 1.2参考文档速读(6):GORM基础和关系建模
- Grails 1.2参考文档速读(7):GORM建模的其余事项及持久化基础
- Grails 1.2参考文档速读(8):GORM中的查询
- Grails 1.2参考文档速读(9):GORM的高级特性及其他
- Grails 1.2参考文档速读(10):Controller
- Grails 1.2参考文档速读(11):GSP
- Grails 1.2参考文档速读(12):URL Mappings
- Grails 1.2参考文档速读(13):Web Flow
- Grails 1.2参考文档速读(14):Web层的其它内容
- Grails 1.2参考文档速读(15):验证
- Grails 1.2参考文档速读(16):服务层
- Grails 1.2参考文档速读(17):测试
- Grails 1.2参考文档速读(18):国际化和安全
- Grails 1.2参考文档速读(19):插件

能否出一个电子书
这个系列能否出一个电子书?这样看起来比较方便 :)
兄台的话说到我心坎里了!
兄台的话说到我心坎里了!但,我考虑到出一个电子书最好还是能够严谨一些,这些帖子有部分即兴的成分在。所以,当时这个念头也就是在心中一转而已。加上目前正在翻译InfoQ的那本Grails的mini书的第二版,手头时间不是太多。打算在那本mini书交稿之后着手此事,估计到时候Grails 1.3会出来,届时就直接融合1.3的吧。
真的不错!
真的不错!辛苦了!
辛苦了,感谢foxgem善始善终完成了这一套速读。我一路看
辛苦了,感谢foxgem善始善终完成了这一套速读。我一路看下来收获不小。