1.ConfigSlurper
ConfigSlurper
是一个实用程序类,用于读取以 Groovy 脚本形式定义的配置文件。就像 Java*.properties
文件的情况一样,ConfigSlurper
允许使用点表示法。但此外,它允许闭包范围的配置值和任意对象类型。
// 点符号的使用
// 使用闭包范围作为点符号的替代品
def config = new ConfigSlurper().parse('''
app.date = new Date()
app.age = 42
app {
name = "Test${42}"
}
''')
assert config.app.date instanceof Date
assert config.app.age == 42
assert 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 = 42
app.name = "Test${42}"
''')
// config.test尚未指定
// 它在被调用时返回一个ConfigObject
assert 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 = true
myProject {
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 = 42
app {
name = "Test${42}"
}
''')
def properties = config.toProperties()
assert properties."app.date" instanceof String
assert 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 event
def listener = {
// ObservableList.ElementEvent及其后代类型与此侦听器相关
if (it instanceof ObservableList.ElementEvent) {
event = it
}
} as PropertyChangeListener
// 注册监听器
def observable = [1, 2, 3] as ObservableList
// 从给定列表创建一个ObservableList
observable.addPropertyChangeListener(listener)
// 触发ObservableList.ElementAddedEvent事件
observable.add 42
assert event instanceof ObservableList.ElementAddedEvent
def elementAddedEvent = event as ObservableList.ElementAddedEvent
assert elementAddedEvent.changeType == ObservableList.ChangeType.ADDED
assert elementAddedEvent.index == 3
assert elementAddedEvent.oldValue == null
assert elementAddedEvent.newValue == 42
:::info
请注意,添加元素实际上会导致触发两个事件。第一个是类型ObservableList.ElementAddedEvent
,第二个是PropertyChangeEvent
通知听众关于属性变化的长度size
。
:::
ObservableList.ElementClearedEvent
事件类型是另一个有趣的类型。每当删除多个元素时,例如在调用 时clear()
,它都会保存从列表中删除的元素。
def event
def listener = {
if (it instanceof ObservableList.ElementEvent) {
event = it
}
} as PropertyChangeListener
def observable = [1, 2, 3] as ObservableList
observable.addPropertyChangeListener(listener)
observable.clear()
assert event instanceof ObservableList.ElementClearedEvent
def elementClearedEvent = event as ObservableList.ElementClearedEvent
assert elementClearedEvent.values == [1, 2, 3]
assert observable.size() == 0
要了解所有支持的事件类型,建议读者查看 JavaDoc 文档或使用中的可观察集合的源代码。
ObservableMap并带有与我们在本节ObservableSet中看到的相同的概念。ObservableList