技术开发 频道

精通 Grails: Grails 事件模型

  启动

  除了构建事件,还可以引入应用程序事件。在每次启动和停止 Grails 时会运行 grails-app/conf/BootStrap.groovy 文件。在文本编辑器中打开 BootStrap.groovy。init 闭包在启动时调用。destroy 闭包在应用程序关闭时调用。

  首先,向闭包添加一些简单文本,如清单 9 所示:

  清单 9. 以 BootStrap.groovy 开始

def init = {
  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 类添加一个字段

class Hotel{
  
String name
}

  现在构建一个 HotelController,如清单 11 所示:

  清单 11. 创建一个 Hotel Controller

class HotelController {
  def scaffold
= Hotel
}

  注意:如果像 “Grails 与遗留数据库” 中讨论的那样禁用 grails-app/conf/DataSource.groovy 中的 dbCreate 变量,本例则应该重新添加它并设置为 update。当然,还有另一种选择是通过手动方式让 Hotel 表与 Hotel 类的更改保持一致。

  现在将清单 12 中的代码添加到 BootStrap.groovy:

  清单 12. 保存和删除 BootStrap.groovy 中的记录

def init = { servletContext ->  
  
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 init = { servletContext ->  
  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 中特定于环境的行为

import grails.util.GrailsUtil

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 服务,通知中央服务器该实例正在运行。或者需要同步来自公共源的本地查找表。这一切都有可能实现。

0
相关文章