1.ConfigSlurper
ConfigSlurper是一个实用程序类,用于读取以 Groovy 脚本形式定义的配置文件。就像 Java*.properties文件的情况一样,ConfigSlurper允许使用点表示法。但此外,它允许闭包范围的配置值和任意对象类型。
// 点符号的使用// 使用闭包范围作为点符号的替代品def config = new ConfigSlurper().parse('''app.date = new Date()app.age = 42app {name = "Test${42}"}''')assert config.app.date instanceof Dateassert config.app.age == 42assert config.app.name == 'Test42'
从上面的示例中可以看出,该parse方法可用于检索groovy.util.ConfigObject实例。这 ConfigObject是一个专门的java.util.Map实现,它要么返回配置的值,要么返回一个新ConfigObject实例,但从不null。
def config = new ConfigSlurper().parse('''app.date = new Date()app.age = 42app.name = "Test${42}"''')// config.test尚未指定// 它在被调用时返回一个ConfigObjectassert config.test != null
如果点是配置变量名称的一部分,则可以使用单引号或双引号对其进行转义。
def config = new ConfigSlurper().parse('''app."person.age" = 42''')assert config.app."person.age" == 42
此外,ConfigSlurper还支持environments. 该environments方法可用于移交一个闭包实例,该实例本身可能由几个部分组成。假设我们想为开发环境创建一个特定的配置值。创建ConfigSlurper实例时,我们可以使用ConfigSlurper(String)构造函数来指定目标环境。
def config = new ConfigSlurper('development').parse('''environments {development {app.port = 8080}test {app.port = 8082}production {app.port = 80}}''')assert config.app.port == 8080
:::info
ConfigSlurper环境不限于任何特定 的环境名称。它仅取决于 ConfigSlurper客户端代码支持和相应解释的值。
:::
该environments方法是内置的,但该registerConditionalBlock方法可用于注册除名称之外的其他方法名称environments。
def slurper = new ConfigSlurper()// 一旦新块被注册ConfigSlurper就可以解析它。slurper.registerConditionalBlock('myProject', 'developers')def config = slurper.parse('''sendMail = truemyProject {developers {sendMail = false}}''')assert !config.sendMail
出于Java集成目的,可以使用toProperties方法将ConfigObject转换为java.util.Properties对象,该对象可能存储在*.properties文本文件。但是请注意,在将配置值添加到新创建的Properties实例中时,配置值会转换为String实例。
def config = new ConfigSlurper().parse('''app.date = new Date()app.age = 42app {name = "Test${42}"}''')def properties = config.toProperties()assert properties."app.date" instanceof Stringassert properties."app.age" == '42'assert properties."app.name" == 'Test42'
2.展开
Expando类可用于创建可动态扩展的对象。尽管它的名字叫ExpandoMetaClass,但它下面并没有使用ExpandoMetaClass。每个Expando对象表示一个独立的、动态构建的实例,可以在运行时使用属性(或方法)进行扩展。
def expando = new Expando()expando.name = 'John'assert expando.name == 'John'
当动态属性注册Closure代码块时,会出现一种特殊情况。一旦注册,就可以像方法调用一样调用它。
def expando = new Expando()expando.toString = { -> 'John' }expando.say = { String s -> "John says: ${s}" }assert expando as String == 'John'assert expando.say('Hi') == 'John says: Hi'
3.可观察的List、Map和Set
Groovy附带了可观察List、Map和Set。当添加、删除或更改元素时,这些集合中的每一个都会触发java.beans.PropertyChangeEvent事件。请注意,PropertyChangeEvent不仅表示某个事件已经发生,而且还包含有关属性名称和某个属性已更改为的旧/新值的信息。
根据发生的更改类型,可观察集合可能会触发更专门的PropertyChangeEvent类型。例如,将元素添加到可观察列表会触发ObservableList.ElementAddedEvent事件。
// 声明一个PropertyChangeEventListener正在捕获触发事件的def eventdef listener = {// ObservableList.ElementEvent及其后代类型与此侦听器相关if (it instanceof ObservableList.ElementEvent) {event = it}} as PropertyChangeListener// 注册监听器def observable = [1, 2, 3] as ObservableList// 从给定列表创建一个ObservableListobservable.addPropertyChangeListener(listener)// 触发ObservableList.ElementAddedEvent事件observable.add 42assert event instanceof ObservableList.ElementAddedEventdef elementAddedEvent = event as ObservableList.ElementAddedEventassert elementAddedEvent.changeType == ObservableList.ChangeType.ADDEDassert elementAddedEvent.index == 3assert elementAddedEvent.oldValue == nullassert elementAddedEvent.newValue == 42
:::info
请注意,添加元素实际上会导致触发两个事件。第一个是类型ObservableList.ElementAddedEvent,第二个是PropertyChangeEvent通知听众关于属性变化的长度size。
:::
ObservableList.ElementClearedEvent事件类型是另一个有趣的类型。每当删除多个元素时,例如在调用 时clear(),它都会保存从列表中删除的元素。
def eventdef listener = {if (it instanceof ObservableList.ElementEvent) {event = it}} as PropertyChangeListenerdef observable = [1, 2, 3] as ObservableListobservable.addPropertyChangeListener(listener)observable.clear()assert event instanceof ObservableList.ElementClearedEventdef elementClearedEvent = event as ObservableList.ElementClearedEventassert elementClearedEvent.values == [1, 2, 3]assert observable.size() == 0
要了解所有支持的事件类型,建议读者查看 JavaDoc 文档或使用中的可观察集合的源代码。
ObservableMap并带有与我们在本节ObservableSet中看到的相同的概念。ObservableList
