groovy 中的 get 与 getProperty
get vs getProperty in groovy
让我吃惊!
根据groovy的文档,groovy可以使用"getProperty"方法获取对象的属性。因此,当我想更改在特殊对象上获取 属性 的行为时,我使用类别 class 来覆盖 "getProperty" 方法。但是,它不起作用。
最后,我发现 groovy 框架使用类别 class 中的 "get" 方法来获取 属性,即使对象不是地图。
我的问题是这是一个错误还是 groovy 就是那样工作。
这是类别 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);
}
}
这是测试代码:
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;
}
这是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" 被调用,而不是 "getProperty"
而且,在实际情况下DynaBeanExtension
是用groovy编译的。 bean的构造是用java编译的。然后通过使用 binding
,我将它放入测试代码中,这是一个由 java 代码执行的运行时脚本。
这发生在编译本身。让我们看一个更简单的例子。
class Main {
static void main(def args) {
Foo foo = new Foo()
foo.str = ""
foo.str
}
}
为Groovy classes
class Foo {
String str
}
如果你反编译 Main
class,你会看到它是
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] =
调用被编译为 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]
为Javaclasses
如果你使用 Java 版本的 class 被调用,就像这样
public class Foo {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
同样的Main
反编译为
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] =
调用被编译为 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(因为它发生在 class 本身),但是如果您想覆盖 getProperty
或 setProperty
,您必须在 metaClass
中执行此操作,而不是 class 本身。您看到的行为是预期的。此代码演示如何重写每个
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
产出
in setter
in getter
in setProperty
in getProperty
并且由于 getProperty
/setProperty
调用(最终)向 getter/setter 进行调度,您可以完全阻止 getter/setter 被调用,例如这个
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
}
产出
in setProperty
in getProperty
让我吃惊!
根据groovy的文档,groovy可以使用"getProperty"方法获取对象的属性。因此,当我想更改在特殊对象上获取 属性 的行为时,我使用类别 class 来覆盖 "getProperty" 方法。但是,它不起作用。 最后,我发现 groovy 框架使用类别 class 中的 "get" 方法来获取 属性,即使对象不是地图。 我的问题是这是一个错误还是 groovy 就是那样工作。
这是类别 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);
}
}
这是测试代码:
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;
}
这是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" 被调用,而不是 "getProperty"
而且,在实际情况下DynaBeanExtension
是用groovy编译的。 bean的构造是用java编译的。然后通过使用 binding
,我将它放入测试代码中,这是一个由 java 代码执行的运行时脚本。
这发生在编译本身。让我们看一个更简单的例子。
class Main {
static void main(def args) {
Foo foo = new Foo()
foo.str = ""
foo.str
}
}
为Groovy classes
class Foo {
String str
}
如果你反编译 Main
class,你会看到它是
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] =
调用被编译为 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]
为Javaclasses
如果你使用 Java 版本的 class 被调用,就像这样
public class Foo {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
同样的Main
反编译为
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] =
调用被编译为 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(因为它发生在 class 本身),但是如果您想覆盖 getProperty
或 setProperty
,您必须在 metaClass
中执行此操作,而不是 class 本身。您看到的行为是预期的。此代码演示如何重写每个
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
产出
in setter
in getter
in setProperty
in getProperty
并且由于 getProperty
/setProperty
调用(最终)向 getter/setter 进行调度,您可以完全阻止 getter/setter 被调用,例如这个
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
}
产出
in setProperty
in getProperty