拦截groovy中的LOCAL属性访问

时间:2012-07-12 16:19:26

标签: groovy

尝试在Groovy中使用属性访问时遇到问题。参加以下课程:

class Foo {
  Map m = [:]
  String bar

  void getProperty(String name) {
    m.get name
  }

  def setProperty(String name, value) {
    m.set name, value
  }

  String getBarString() {
    return bar // local access, does not go through getProperty()
  }
}

它会覆盖getter和setter,只是将值放入Map而不是对象的普通属性空间。在摘要中,这有点愚蠢,但想象一下,不是将数据放入地图,而是将数据保存到数据库或其他有用的地方。

不幸的是,以下代码现在不起作用:

foo = new Foo()
foo.bar = "blerg" // using foo.bar invokes the setProperty interceptor
assert foo.bar == "blerg" // this will work fine as foo.bar here uses the getProperty interceptor
assert foo.getBarString() == "blerg" // explosion and fire!  getBarString accesses bar locally without going through the getProperty interceptor so null will actually be returned.

当然有解决方法,setProperty可以同时设置MetaProperty和Map值等。但是,我想到的所有策略都需要程序员多加注意,以确保他们正在访问类属性的确切方式。

此外,Groovy中的一些内置很棒的东西(例如@Delegate)使用直接的MetaProperty访问而不是通过getProperty,因此以下内容永远不会起作用:

class Meep {
  String getMyMeep() {
    return "MEEP!!!"
  }
}

class Foo {
  Map m = [:]
  String bar
  @Delegate Meep meep

  void getProperty(String name) {
    m.get name
  }

  def setProperty(String name, value) {
    m.set name, value
  }

  String getBarString() {
    return bar
  }
}

foo = new Foo()
foo.meep = new Meep() // uses setProperty and so does not place the Meep in the Map m
foo.getMyMeep()

在最后一行抛出空指针异常,因为@Delegate使用MetaProperty直接访问(实际上是this.meep.getMyMeep()而不是getProperty拦截器。不幸的是'meep'为null,尽管getProperty('meep')会不是。

总之,我正在寻找的是一种解决以下标准的策略:

  • 拦截属性读/写以启用自动替代数据存储
  • 其他开发者的透明或接近透明的界面(我不想让其他人的生活变得更加困难)
  • 允许使用MetaProperty / this / etc进行变量的本地访问。访问方法

提前致谢!

2 个答案:

答案 0 :(得分:1)

您可以使用

foo.@meep = new Meep()

为了绕过setProperty方法直接访问属性。 尽管foo.meep仍会触发set/getProperty,但这并不能完全解决您的问题。

你可以采用的另一种方法是直接使用会面的getter和setter,即

foo.setMeep(new Meep())

因此,一种统一的方法是将所有变量定义为私有并使用get / set * PropertyName *

答案 1 :(得分:1)

通过使用AST转换,我可以执行以下操作:

  • 走一个类的结构并将所有本地字段重命名为x - &gt; <强> X 即可。
  • 为每个重命名的字段添加一个getter / setter,如下所示

    def get_ x _(){   <强> X }

...以 x 作为字段而不是Groovy属性进行访问 - 现在在以下类中应用转换

class Foo {

  def x
  def y
  Map m = [:]
  @Delegate Date date // for testing if non-local fields work

  def getProperty(String name) {
    if (this.respondsTo("get__${name}__")) // if this is one of our custom fields
      return "get__${name}__"()
    "get${Verifier.capitalize(name)}"() // pass to specific getter method
  }

  void setProperty {
    if (this.respondsTo("set__${name}__")) {
      "set__${name}__"(value)
      m[name] = value
      if (name == "x") y = x + 1
      return
    }
    "set${Verifier.capitalize(name)}"(value)
  }
}
  • 现在运行这样的测试方法:

    public void testAST(){   def file = new File('。/ src / groovy / TestExample.groovy')   GroovyClassLoader调用者=新的GroovyClassLoader()   def clazz = invoker.parseClass(file)   def out = clazz.newInstance()

    out.x = 10   断言out.y == 11   out.y = 5   断言out.y == 5   out.x = 2   断言out.m.containsKey('x')   断言out.m.x == 2   断言out.m.y == 3

    out.date = new Date()   断言out.time&amp;&amp; out.time&gt; 0 }

一切都应该成功,包括获得更新,正确访问日期委托方法时间等等。

-Glenn