返回介绍

3. 实用工具

发布于 2025-01-04 00:44:51 字数 5451 浏览 0 评论 0 收藏 0

3.1 ConfigSlurper

ConfigSlurper 是一种能够读取 Groovy 脚本形式配置文件的工具类。就像与 Java 中 *.properties 文件那样, ConfigSlurper 可以使用点标记法。但除此之外,它还能支持闭包范围的配置值以及任意对象类型。

def config = new ConfigSlurper().parse('''
  app.date = new Date()  // 1⃣️
  app.age  = 42
  app {          // 2⃣️
    name = "Test${42}"
  }
''')

assert config.app.date instanceof Date
assert config.app.age == 42
assert config.app.name == 'Test42'

1⃣️ 使用点标记法
2⃣️ 使用闭包范围作为点标记法的替代方法

如上例所示, parse 方法可以用来获取 groovy.util.ConfigObject 实例。 ConfigObjectjava.util.Map 的一种专门实现,要么返回配置值,要么返回一个新的 ConfigObject 实例,但永远不会返回 null 值。

def config = new ConfigSlurper().parse('''
  app.date = new Date()
  app.age  = 42
  app.name = "Test${42}"
''')

assert config.test != null    // 1⃣️

1⃣️ config.test 尚未被被指定为在调用时返回一个 ConfigObject 实例

当配置变量名包含点时,可以用单引号或双引号对该变量名实施转义处理:

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

ConfigSlurper 环境并不局限于任何特定的环境名称。它只依赖于其值受支持并被解析的 ConfigSlurper 客户端代码。

environments 方法是内建的,但 registerConditionalBlock 方法可以用来注册除 environments 之外的其他方法名。

def slurper = new ConfigSlurper()
slurper.registerConditionalBlock('myProject', 'developers')  // 1⃣️  

def config = slurper.parse('''
  sendMail = true

  myProject {
     developers {
       sendMail = false
     }
  }
''')

assert !config.sendMail

1⃣️ 一旦新语句块注册成功, ConfigSlurper 就能解析它

为了能与 Java 整合,可以采用 toProperties 方法将 ConfigObject 对象转化为一种可能储存在 *.properties 文本文件中的 java.util.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'

3.2 Expando

Expando 类可创建动态可扩展对象。虽然有 Expando 这样的名称,但它却不能使用 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.3 可观测的列表、映射与集

Groovy 提供了可观测的列表、映射与集。对于这其中每一种集合,在添加、删除或改变元素时,都会触发 java.beans.PropertyChangeEvent 事件。 PropertyChangeEvent 并不仅仅是表示某个特定事件发生的标志,更重要的是,它记录着特定属性的名称以及该属性改变前后的值。

根据发生改变的类型,可观测集合可以发出更多的专门类型的 PropertyChangeEvent 事件。比如,可观测列表上添加元素,就会发出一个 ObservableList.ElementAddedEvent 事件。

def event                       1⃣️              
def listener = {
  if (it instanceof ObservableList.ElementEvent)  {  2⃣️
    event = it
  }
} as PropertyChangeListener

def observable = [1, 2, 3] as ObservableList     3⃣️
observable.addPropertyChangeListener(listener)    4⃣️

observable.add 42                  5⃣️

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

1⃣️ 声明一个捕获触发事件的 PropertyChangeEventListener 。 2⃣️ ObservableList.ElementEvent 与其子类型都跟该侦听器有关。
3⃣️ 注册侦听器。
4⃣️ 为指定列表创建一个 ObservableList
5⃣️ 触发 ObservableList.ElementAddedEvent 事件。

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

要想深入了解所有受支持的事件类型,读者需要查看相关 Java 文档或实际中使用的可观测集合的源代码。

与本节介绍的 ObservableList 相同, ObservableMapObservableSet 也具有相似的概念。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文