得到vs getProperty在groovy [英] get vs getProperty in groovy

查看:77
本文介绍了得到vs getProperty在groovy的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



根据groovy的文档,groovy可以使用getProperty方法来获取对象的属性。所以当我想改变在特殊对象上获取属性的行为时,我使用类别类来覆盖getProperty方法。但是,它不起作用。
最后,我发现groovy框架在类别类中使用get方法来获取属性,即使对象不是地图。
我的问题是,这是一个错误或groovy就是这样工作。

这是类别类。

  class DynaBeanExtension {

public static void setProperty(DynaBean bean,String propertyName,def newValue){
try {
PropertyUtilsBean pu = null;
if(bean的instanceof CustomWrapDynaBean){
pu = bean.propertyUtilsBean;

if(pu!= null){
pu.setProperty(bean,propertyName,newValue);
} else {
PropertyUtils.setProperty(bean,propertyName,newValue);
}
} catch(IllegalArgumentException ex){
bean.propertyMissing(propertyName,newValue);



public static def getProperty(DynaBean bean,String propertyName){
try {
PropertyUtilsBean pu = null;
if(bean的instanceof CustomWrapDynaBean){
pu = bean.propertyUtilsBean;
}
if(pu!= null){
return pu.getProperty(bean,propertyName);
} else {
return PropertyUtils.getProperty(bean,propertyName);
}
} catch(IllegalArgumentException ex){
return bean.propertyMissing(propertyName);



public static def get(DynaBean bean,String propertyName){
try {
PropertyUtilsBean pu = null;
if(bean的instanceof CustomWrapDynaBean){
pu = bean.propertyUtilsBean;
}
if(pu!= null){
return pu.getProperty(bean,propertyName);
} else {
return PropertyUtils.getProperty(bean,propertyName);
}
} catch(IllegalArgumentException ex){
return bean.propertyMissing(propertyName);


这是测试代码:

  public static class TestSubClass {

private final int e = 3,f = 4;
private final Map< String,Object> m = new HashMap<>();

public int getE(){
return e;
}

public int getF(){$​​ b $ b return f;
}

public Map< String,Object> getM(){
return m;

$ b @Override
public String toString(){
returnTestSubClass {+e =+ e +,f =+ f + ,m =+ m +'}';



$ b public static class TestClass {

private final int a = 1;
private final TestSubClass b = new TestSubClass();

public int getA(){
return a;
}

public TestSubClass getB(){
return b;

$ b @Override
public String toString(){
returnTestClass {+a =+ a +,b =+ b + '}';
}

}

Map< String,String> pMap = new HashMap<>();
pMap.put(b.e,c);
PropertyUtilsBean pu = new PropertyUtilsBean();
pu.setResolver(新的ExResolver(pMap));
TestClass testObj = new TestClass();
DynaBean bean = new CustomWrapDynaBean(testObj,pu);

int c = use(DynaBeanExtension){
bean.c;
}

这是ExResolver的代码:

  public class ExResolver implements Resolver {

private static final char NESTED ='。';
private static final char MAPPED_START ='(';
private static final char MAPPED_END =')';
private static final char INDEXED_START ='[';
private static final char INDEXED_END =']';

私人最终解析器解析器;
private final Map< String,String> PMAP;

public ExResolver(Map< String,String> pMap){
this(new DefaultResolver(),pMap);
}

public ExResolver(解析器解析器,Map< String,String> pMap){
this.resolver =解析器;
this.pMap = new HashMap<>(pMap);
}

private String resolveExpr(String expression){
for(Map.Entry< String,String> entry:pMap.entrySet()){
if( expression.startsWith(entry.getValue())){
String to = entry.getValue();
if(expression.length()== entry.getValue()。length()){
return entry.getKey();
} else {
int toTest = expression.codePointAt(to.length());
if(toTest == NESTED || toTest == MAPPED_START || toTest == INDEXED_START){
return entry.getKey()+ expression.substring(to.length(),expression.length() );
} else {
return expression;
}
}
}
}
返回表达式;
}

@Override
public int getIndex(String expression){
expression = resolveExpr(expression);
返回resolver.getIndex(表达式);
}

@Override
public String getKey(String expression){
expression = resolveExpr(expression);
返回resolver.getKey(表达式);
}

@Override
public String getProperty(String expression){
expression = resolveExpr(expression);
返回resolver.getProperty(表达式);
}

@Override
public boolean hasNested(String expression){
expression = resolveExpr(expression);
返回resolver.hasNested(表达式);
}

@Override
public boolean isIndexed(String expression){
expression = resolveExpr(expression);
返回resolver.isIndexed(表达式);
}

@Override
public boolean isMapped(String expression){
expression = resolveExpr(expression);
返回resolver.isMapped(表达式);
}

@Override
public String next(String expression){
expression = resolveExpr(expression);
返回resolver.next(表达式);
}

@Override
public String remove(String expression){
expression = resolveExpr(expression);
返回resolver.remove(表达式);
}

}

get被调用,而不是getProperty

更重要的是,在真实情况下,使用groovy编译 DynaBeanExtension 。 bean的构造是用java编译的。然后通过使用 binding ,我将它放入测试代码中,该代码是由java代码执行的运行时脚本。 >解决方案

这发生在编译本身。让我们来看一个更简单的例子。

  class Main {
static void main(def args){
Foo foo = new Foo()
foo.str =
foo.str
}
}

  class Foo {
String str
}

如果你反编译 Main 类,你会发现它是

  public class Main实现GroovyObject {
public Main(){
Main this;
CallSite [] arrayOfCallSite = $ getCallSiteArray();
MetaClass localMetaClass = $ getStaticMetaClass();
this.metaClass = localMetaClass;


public static void main(String ... args){
CallSite [] arrayOfCallSite = $ getCallSiteArray();
Foo foo =(Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite [0] .callConstructor(Foo.class),Foo.class);
String str =;
ScriptBytecodeAdapter.setGroovyObjectProperty(str,Main.class,foo,(String)str);

arrayOfCallSite [1] .callGroovyObjectGetProperty(foo);




$ 。[property] = 调用被编译为 ScriptBytecodeAdapter.setGroovyObjectProperty ,然后调用链 MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method .invoke > [setter]



。 [property] 调用被编译为一个 arrayOfCallSite [1] .callGroovyObjectGetProperty ,然后调用链接
AbstractCallSite.callGroovyObjectGetProperty > GetEffectivePogoPropertySite.getProperty > MethodMetaProperty $ GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]



Java类



如果您使用正在调用的类的Java版本ed,像这样

  public class Foo {
private String str;

public String getStr(){
return str;
}

public void setStr(String str){
this.str = str;




$ b $ p $相同主要
code>反编译为

  public class Main实现GroovyObject {
public Main (){
主要;
CallSite [] arrayOfCallSite = $ getCallSiteArray();
MetaClass localMetaClass = $ getStaticMetaClass();
this.metaClass = localMetaClass;


public static void main(String ... args){
CallSite [] arrayOfCallSite = $ getCallSiteArray();
Foo foo =(Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite [0] .callConstructor(Foo.class),Foo.class);
String str =;
ScriptBytecodeAdapter.setProperty(str,null,foo,(String)str);

arrayOfCallSite [1] .callGetProperty(foo);




$ 。[property] = 调用被编译为 ScriptBytecodeAdapter.setProperty ,然后调用链 [Class] .setProperty > InvokerHelper.setProperty - > MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]



并且编译。[property] arrayOfCallSite [1] .callGroovyObjectGetProperty ,然后调用链
AbstractCallSite.callGetProperty > GetEffectivePojoPropertySite.getProperty > MethodMetaProperty $ GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter ]



更正您的代码

正如你从这些调度链中看到的那样,你已经正确地重写了getter(因为它发生在类本身中),但是如果你想重写 getProperty setProperty ,您必须在 metaClass 中执行此操作,而不是类本身。你看到的行为是预期的。此代码演示了如何覆盖每个

  class Foo {
String bar
}

//覆盖类别
中的setter @Category(Foo)
class FooCategory {
public String getBar(){
printlnin getter
}
public void setBar(String bar){
printlnin setter
}
}
use(FooCategory){
Foo foo = new Foo( )
foo.bar =
foo.bar
}

//覆盖使用metaClass
Foo.metaClass.getProperty {String pname - > ;
printlnin getProperty
}
Foo.metaClass.setProperty {String pname,Object pValue - >
printlnin setProperty
}
Foo foo = new Foo()
foo.bar =
foo.bar


$ p
$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b中的getter
中的setProperty
中的getProperty

由于 getProperty / setProperty 调用使调度(最终)到达getter / setter,可以防止调用getter / setter毕竟,像这样

  class Foo {
String bar
}

Foo.metaClass.getProperty {String pname - >
printlnin getProperty
}
Foo.metaClass.setProperty {String pname,Object pValue - >
printlnin setProperty
}

@Category(Foo)
class FooCategory {
String getBar(){
printlnin getter
}
void setBar(String bar){
printlnin setter
}
}

use(FooCategory){
Foo foo = new Foo()
foo.bar =hi foo1
foo.bar
}
$ b

输出

  setProperty 
getProperty


It surprise me!

According to the document of groovy, groovy may use "getProperty" method to get the property of a object. So when I want to change the behavier of getting property on the special object, I use a category class to override the "getProperty" method. However, it does not work. At last, I found groovy framework use the "get" method in the category class to get property, even if the object is not a map. My question is that is it a bug or groovy just work like that.

This is the category class.

class DynaBeanExtension {

    public static void setProperty(DynaBean bean, String propertyName, def newValue) {
        try {
            PropertyUtilsBean pu = null;
            if (bean instanceof CustomWrapDynaBean) {
                pu = bean.propertyUtilsBean;
            }
            if (pu != null) {
                pu.setProperty(bean, propertyName, newValue);
            } else {
                PropertyUtils.setProperty(bean, propertyName, newValue);
            }
        } catch (IllegalArgumentException ex) {
            bean.propertyMissing(propertyName, newValue);
        }
    }

    public static def getProperty(DynaBean bean, String propertyName) {
        try {
            PropertyUtilsBean pu = null;
            if (bean instanceof CustomWrapDynaBean) {
                pu = bean.propertyUtilsBean;
            }
            if (pu != null) {
                return pu.getProperty(bean, propertyName);
            } else {
                return PropertyUtils.getProperty(bean, propertyName);
            }
        } catch (IllegalArgumentException ex) {
            return bean.propertyMissing(propertyName);
        }
    }

    public static def get(DynaBean bean, String propertyName) {
        try {
            PropertyUtilsBean pu = null;
            if (bean instanceof CustomWrapDynaBean) {
                pu = bean.propertyUtilsBean;
            }
            if (pu != null) {
                return pu.getProperty(bean, propertyName);
            } else {
                return PropertyUtils.getProperty(bean, propertyName);
            }
        } catch (IllegalArgumentException ex) {
            return bean.propertyMissing(propertyName);
        }
    }

This is the test code:

public static class TestSubClass {

    private final int e = 3, f = 4;
    private final Map<String, Object> m = new HashMap<>();

    public int getE() {
        return e;
    }

    public int getF() {
        return f;
    }

    public Map<String, Object> getM() {
        return m;
    }

    @Override
    public String toString() {
        return "TestSubClass{" + "e=" + e + ", f=" + f + ", m=" + m + '}';
    }

}

public static class TestClass {

    private final int a = 1;
    private final TestSubClass b = new TestSubClass();

    public int getA() {
        return a;
    }

    public TestSubClass getB() {
        return b;
    }

    @Override
    public String toString() {
        return "TestClass{" + "a=" + a + ", b=" + b + '}';
    }

}

Map<String, String> pMap = new HashMap<>();
pMap.put("b.e", "c");
PropertyUtilsBean pu = new PropertyUtilsBean();
pu.setResolver(new ExResolver(pMap));
TestClass testObj = new TestClass();
DynaBean bean = new CustomWrapDynaBean(testObj, pu);

int c = use(DynaBeanExtension) {
    bean.c;
}

This is the code of ExResolver:

public class ExResolver implements Resolver {

    private static final char NESTED = '.';
    private static final char MAPPED_START = '(';
    private static final char MAPPED_END = ')';
    private static final char INDEXED_START = '[';
    private static final char INDEXED_END = ']';

    private final Resolver resolver;
    private final Map<String, String> pMap;

    public ExResolver(Map<String, String> pMap) {
        this(new DefaultResolver(), pMap);
    }

    public ExResolver(Resolver resolver, Map<String, String> pMap) {
        this.resolver = resolver;
        this.pMap = new HashMap<>(pMap);
    }

    private String resolveExpr(String expression) {
        for (Map.Entry<String, String> entry : pMap.entrySet()) {
            if (expression.startsWith(entry.getValue())) {
                String to = entry.getValue();
                if (expression.length() == entry.getValue().length()) {
                    return entry.getKey();
                } else {
                    int toTest = expression.codePointAt(to.length());
                    if (toTest == NESTED || toTest == MAPPED_START || toTest == INDEXED_START) {
                        return entry.getKey() + expression.substring(to.length(), expression.length());
                    } else {
                        return expression;
                    }
                }
            }
        }
        return expression;
    }

    @Override
    public int getIndex(String expression) {
        expression = resolveExpr(expression);
        return resolver.getIndex(expression);
    }

    @Override
    public String getKey(String expression) {
        expression = resolveExpr(expression);
        return resolver.getKey(expression);
    }

    @Override
    public String getProperty(String expression) {
        expression = resolveExpr(expression);
        return resolver.getProperty(expression);
    }

    @Override
    public boolean hasNested(String expression) {
        expression = resolveExpr(expression);
        return resolver.hasNested(expression);
    }

    @Override
    public boolean isIndexed(String expression) {
        expression = resolveExpr(expression);
        return resolver.isIndexed(expression);
    }

    @Override
    public boolean isMapped(String expression) {
        expression = resolveExpr(expression);
        return resolver.isMapped(expression);
    }

    @Override
    public String next(String expression) {
        expression = resolveExpr(expression);
        return resolver.next(expression);
    }

    @Override
    public String remove(String expression) {
        expression = resolveExpr(expression);
        return resolver.remove(expression);
    }

}

"get" is invoked, not "getProperty"

What's more, in the real situation DynaBeanExtension is compiled with groovy. The construction of bean is compiled with java. Then by using binding, I put it into the test code which is a runtime script executed by java code.

解决方案

This happens in the compilation itself. Let's look at a simpler example.

class Main {
    static void main(def args) {
        Foo foo = new Foo()
        foo.str = ""
        foo.str
    }
}

For Groovy classes

class Foo {
    String str
}

If you decompile the Main class, you'll see it is

public class Main implements GroovyObject {
    public Main() {
        Main this;
        CallSite[] arrayOfCallSite = $getCallSiteArray();
        MetaClass localMetaClass = $getStaticMetaClass();
        this.metaClass = localMetaClass;
    }

    public static void main(String... args) {
        CallSite[] arrayOfCallSite = $getCallSiteArray();
        Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
        String str = "";
        ScriptBytecodeAdapter.setGroovyObjectProperty(str, Main.class, foo, (String)"str");

        arrayOfCallSite[1].callGroovyObjectGetProperty(foo);
    }
}

A .[property] = call gets compiled to a ScriptBytecodeAdapter.setGroovyObjectProperty, that in turn calls the chain MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]

And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain AbstractCallSite.callGroovyObjectGetProperty > GetEffectivePogoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]

For Java classes

If you use a Java version of the class being called, like this

public class Foo {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}

The same Main decompiles to

public class Main implements GroovyObject {
    public Main() {
        Main this;
        CallSite[] arrayOfCallSite = $getCallSiteArray();
        MetaClass localMetaClass = $getStaticMetaClass();
        this.metaClass = localMetaClass;
    }

    public static void main(String... args) {
        CallSite[] arrayOfCallSite = $getCallSiteArray();
        Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
        String str = "";
        ScriptBytecodeAdapter.setProperty(str, null, foo, (String)"str");

        arrayOfCallSite[1].callGetProperty(foo);
    }
}

A .[property] = call gets compiled to a ScriptBytecodeAdapter.setProperty, that in turn calls the chain [Class].setProperty > InvokerHelper.setProperty -> MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]

And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain AbstractCallSite.callGetProperty > GetEffectivePojoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]

To correct your code

As you can see from these dispatch chains, you've overridden the getter correctly (since it happens in the class itself), but if you want to override getProperty or setProperty, you have to do this in metaClass, and not the class itself. The behavior you're seeing is expected. This code demonstrates how to override each

class Foo {
    String bar
}

// override using setter in category
@Category(Foo)
class FooCategory {
    public String getBar() {
        println "in getter"
    }
    public void setBar(String bar) {
        println "in setter"
    }
}
use (FooCategory) {
    Foo foo = new Foo()
    foo.bar = ""
    foo.bar
}

// override using metaClass
Foo.metaClass.getProperty { String pname ->
    println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
    println "in setProperty"
}
Foo foo = new Foo()
foo.bar = ""
foo.bar

outputs

in setter
in getter
in setProperty
in getProperty

And because the getProperty/setProperty call makes the dispatch (eventually) to the getter/setter, you can prevent the getter/setter from being called at all, like this

class Foo {
    String bar
}

Foo.metaClass.getProperty { String pname ->
    println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
    println "in setProperty"
}

@Category(Foo)
class FooCategory {
    String getBar() {
        println "in getter"
    }
    void setBar(String bar) {
        println "in setter"
    }
}

use (FooCategory) {
    Foo foo = new Foo()
    foo.bar = "hi foo1"
    foo.bar
}

outputs

in setProperty
in getProperty

这篇关于得到vs getProperty在groovy的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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