拦截groovy中的LOCAL属性访问 [英] intercepting LOCAL property access in groovy

查看:111
本文介绍了拦截groovy中的LOCAL属性访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  class Foo {
Map m = [:]
String bar
$ b $ void getProperty(String name){
m.get name
}
$ b $ def setProperty(String name,value){
m.set名称,值
}

字符串getBarString(){
返回栏//本地访问,不经过getProperty()
}
}

它重写getter和setter,只需将值放入Map中而不​​是置入对象的常规属性空间。在抽象中,这有点愚蠢,但想象一下,不是将数据放入地图中,而是将其保存到数据库或其他有用的地方。



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

  foo = new Foo()
foo.bar =blerg/ /使用foo.bar调用setProperty拦截器
assert foo.bar ==blerg//这会正常工作,因为foo.bar在这里使用getProperty拦截器
assert foo.getBarString()== blerg//爆炸和火灾! getBarString在本地访问栏而不通过getProperty拦截器,所以实际上会返回null。

当然有解决方法,setProperty可以设置MetaProperty和Map值等。 ,我想过的所有策略都需要程序员特别小心,以确保他们正确地访问类属性。



另外,Groovy中的一些内置功能(比如@Delegate)使用直接的MetaProperty访问,而不是通过getProperty访问,所以以下内容将无法使用:

  class Meep {
String getMyMeep(){
returnMEEP !!!



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

void getProperty(String name){
m.get name
}
$ b $ def setProperty(String name,value){
m.set name,value

$ b $ String getBarString(){
return bar
}
}

foo = new Foo()
foo.meep = new Meep()//使用setProperty,因此不在地图中放置Meep m
foo.getMyMeep()



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



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




  • 截取属性读取/写入以启用自动替代数据存储功能
  • 透明或接近tr对于其他开发人员来说,我不希望让其他人的生活更加艰难。
  • 允许使用MetaProperty / this /等进行局部变量访问。访问方法



预先感谢!

解决方案

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


  • 遍历类结构并将所有本地字段重命名为x - > x

  • 为每个重命名的字段添加一个getter / setter,像这样

    def get_ x _(){
    x
    }




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

  class Foo {

def x
def y
Map m = [:]
@Delegate日期日期//用于测试非本地字段是否工作
$ b def getProperty(String name){
if(this.respondsTo(get __ $ {name} __ ))//如果这是我们的自定义字段之一
returnget __ $ {name} __()
get $ {Verifier.capitalize(name)}()// pass t o特定的getter方法
}

void setProperty {
if(this.respondsTo(set __ $ {name} __)){
set __ $ {name } __(value)
m [name] = value $ b $ if(name ==x)y = x + 1
return
}
set $ {Verifier.capitalize(name)}(value)
}
}




  • 现在运行如下测试方法:

    public void testAST(){
    def file = new File( './src/groovy/TestExample.groovy')
    GroovyClassLoader invoker = new GroovyClassLoader()
    def clazz = invoker.parseClass(file)
    def out = clazz.newInstance() p>

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



    out.date = new Date()
    断言out.time&& out.time> 0



    一切工作都应该包括m更新,日期委托方法时间正常访问等。



    -Glenn


    I am running into a problem while trying to use property access in Groovy. Take the following class:

    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()
      }
    }
    

    It overrides the getter and setter to simply place the values into a Map rather than into the object's normal property space. In the abstract this is a bit silly, but imagine that instead of placing the data into a map we were persisting it to a DB or something else useful.

    Unfortunately, the following code now won't work:

    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.
    

    Certainly there are workarounds for this, setProperty could set both the MetaProperty and the Map value, etc. However, all of the strategies I've thought of require a lot of extra caution from the programmer to make sure they are accessing class properties in the exact way that they mean to.

    Furthermore, some of the built in awesome stuff in Groovy (like @Delegate for example) uses direct MetaProperty access rather than going through getProperty so the following would never work:

    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()
    

    A null pointer exception is thrown on the last line as @Delegate uses MetaProperty direct access (effectively this.meep.getMyMeep() rather than the getProperty interceptor. Unfortunately 'meep' is null, though getProperty('meep') would not be.

    In short what I'm looking for is a strategy to solve the following criteria:

    • intercept property read/write to enable automatic alternative data storage
    • transparent or near-transparent interface for other developers (I don't want to make other folks' lives significantly harder)
    • allow for local access of variables using the MetaProperty/this/etc. access methods

    Thanks in advance!

    解决方案

    By using an AST Transformation I can do the following:

    • walk a class's structure and rename all local fields to something like x -> x.
    • add a getter/setter for each renamed field like this

      def get_x_() { x }

    ...in order to access x as a field rather than as a Groovy property - now apply the transformation on the following class

    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)
      }
    }
    

    • now run a testing method like this:

      public void testAST() { def file = new File('./src/groovy/TestExample.groovy') GroovyClassLoader invoker = new GroovyClassLoader() def clazz = invoker.parseClass(file) def out = clazz.newInstance()

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

      out.date = new Date() assert out.time && out.time > 0 }

    And everything should work out including m getting updated, date delegate method time getting accessed properly, etc.

    -Glenn

    这篇关于拦截groovy中的LOCAL属性访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆