启动
除了构建事件,还可以引入应用程序事件。在每次启动和停止 Grails 时会运行 grails-app/conf/BootStrap.groovy 文件。在文本编辑器中打开 BootStrap.groovy。init 闭包在启动时调用。destroy 闭包在应用程序关闭时调用。
首先,向闭包添加一些简单文本,如清单 9 所示:
清单 9. 以 BootStrap.groovy 开始
println "### Starting up"
}
def destroy = {
println "### Shutting down"
}
输入 grails run-app 启动应用程序。应该会程序末尾附近看到 ### Starting Up 消息。
现在按 CTRL+C。看到 ### Shutting Down 消息了吗?我没有看到。问题在于 CTRL+C 会突然停止服务器,而不调用 destroy 闭包。Rest 确保在应用服务器关闭时会调用此闭包。但无需输入 grails war 并在 Tomcat 或 IBM®WebSphere® 中加载 WAR 来查看 destroy 事件。
要查看 init 和 destroy 事件触发,输入 grails interactive 以交互模式启动 Grails。现在输入 run-app 启动应用程序,输入 exit 关闭服务器。以交互模式运行会大大加快开发过程,因为 JVM 一直在运行并随时可用。其中一个优点是,与使用 CTRL+C 强硬方法相比,应用程序关闭得更恰当。
在启动期间向数据库添加记录
使用 BootStrap.groovy 脚本除了提供简单的控制台输出,还能做什么呢?通常,人们使用这些挂钩将记录插入数据库中。
首先,向先前创建的 Hotel 类中添加一个名称字段,如清单 10 所示:
清单 10. 向 Hotel 类添加一个字段
String name
}
现在构建一个 HotelController,如清单 11 所示:
清单 11. 创建一个 Hotel Controller
def scaffold = Hotel
}
注意:如果像 “Grails 与遗留数据库” 中讨论的那样禁用 grails-app/conf/DataSource.groovy 中的 dbCreate 变量,本例则应该重新添加它并设置为 update。当然,还有另一种选择是通过手动方式让 Hotel 表与 Hotel 类的更改保持一致。
现在将清单 12 中的代码添加到 BootStrap.groovy:
清单 12. 保存和删除 BootStrap.groovy 中的记录
new Hotel(name:"Marriott").save()
new Hotel(name:"Sheraton").save()
}
def destroy = {
Hotel.findByName("Marriott").delete()
Hotel.findByName("Sheraton").delete()
}
在接下来的几个示例中,需要一直打开 MySQL 控制台并观察数据库。输入 mysql --user=grails -p --database=trip 登录(记住,密码是 server)。然后执行以下步骤:
1.如果 Grails 还没有运行就启动它。
2.输入 show tables; 确认已创建 Hotel 表。
3.输入 desc hotel; 查看列和数据类型。
4.输入 select from hotel; 确认记录已插入。
5.输入 delete from hotel; 删除所有记录。
BootStrap.groovy 中的防故障数据库插入和删除
在 BootStrap.groovy 中执行数据库插入和删除操作时可能需要一定的防故障措施。如果在插入之前没有检查记录是否存在,可能会在数据库中得到重复项。如果试着删除不存在的记录,会看到在控制台上抛出恶意异常。清单 13 说明了如何执行防故障插入和删除:
清单 13. 防故障插入和删除
def hotel = Hotel.findByName("Marriott")
if(!hotel){
new Hotel(name:"Marriott").save()
}
hotel = Hotel.findByName("Sheraton")
if(!hotel){
new Hotel(name:"Sheraton").save()
}
}
def destroy = {
def hotel = Hotel.findByName("Marriott")
if(hotel){
Hotel.findByName("Marriott").delete()
}
hotel = Hotel.findByName("Sheraton")
if(hotel){
Hotel.findByName("Sheraton").delete()
}
}
如果调用 Hotel.findByName("Marriott"),并且 Hotel 不存在表中,就会返回一个 null 对象。下一行 if(!hotel) 只有在值非空时才等于 true。这确保了只在新 Hotel 还不存在时才保存它。在 destroy 闭包中,执行相同的测试,确保不删除不存在的记录。
在 BootStrap.groovy 中执行特定于环境的行为
如果希望行为只在以特定的模式中运行时才发生,可以借助 GrailsUtil 类。在文件顶部导入 grails.util.GrailsUtil。静态 GrailsUtil.getEnvironment() 方法(由于 Groovy 的速记 getter 语法,简写为 GrailsUtil.environment)指明运行的模式。将此与 switch 语句结合起来,如清单 14 所示,可以在 Grails 启动时让特定于环境的行为发生:
清单 14. BootStrap.groovy 中特定于环境的行为
class BootStrap {
def init = { servletContext ->
switch(GrailsUtil.environment){
case "development":
println "#### Development Mode (Start Up)"
break
case "test":
println "#### Test Mode (Start Up)"
break
case "production":
println "#### Production Mode (Start Up)"
break
}
}
def destroy = {
switch(GrailsUtil.environment){
case "development":
println "#### Development Mode (Shut Down)"
break
case "test":
println "#### Test Mode (Shut Down)"
break
case "production":
println "#### Production Mode (Shut Down)"
break
}
}
}
现在具备只在测试模式下插入记录的条件。但不要在此停住。我通常在 XML 文件中外部化测试数据。将这里所学到的知识与 “Grails 与遗留数据库” 中的 XML 备份和还原脚本相结合,就会得到了一个功能强大的测试平台(testbed)。
因为 BootStrap.groovy 是一个可执行的脚本,而不是被动配置文件,所以理论上可以在 Groovy 中做任何事情。您可能需要在启动时调用一个 Web 服务,通知中央服务器该实例正在运行。或者需要同步来自公共源的本地查找表。这一切都有可能实现。