2015-06-09 5 views
4

Меня это удивляет!get vs getProperty in groovy

В соответствии с документом groovy, groovy может использовать метод getProperty для получения свойства объекта. Поэтому, когда я хочу изменить поведение получения свойства на специальный объект, я использую класс категории для переопределения метода getProperty. Однако это не работает. Наконец, я обнаружил, что groovy framework использует метод 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() { 
     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); 
    } 

} 

"получить" вызывается, а не "GetProperty"

Что больше , в реальной ситуации DynaBeanExtension составлен с groovy. Построение боба составлено с помощью java. Затем, используя binding, я поместил его в тестовый код, который представляет собой сценарий выполнения, выполняемый java-кодом.

+1

ну вы можете добавить код на вопрос и описать там, что не работает – cfrick

+0

нормально. Я помещаю код в ответ ответа –

+0

нет, вы этого не делаете. пожалуйста, отредактируйте этот вопрос (http://stackoverflow.com/posts/30722700/edit). SO - это не форум, а сайт QA. – cfrick

ответ

1

Это происходит в самой компиляции. Давайте посмотрим на более простой пример.

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

Для Groovy классов

class Foo { 
    String 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.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-версию вызываемого класса, например

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

.[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]

Чтобы исправить ваш код

Как вы можете видеть из этих диспетчерских цепей, вы заменяете поглотитель правильно (так как это происходит в классе сам), но если вы хотите переопределить getProperty или setProperty, вы должны сделать это в metaClass, а не в самом классе. Ожидается поведение, которое вы видите.Этот код показывает, как переопределить каждый из

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 делает отправку (в конечном счете) в геттер/сеттер, вы можете предотвратить геттер/сеттер от того, называется вообще, как этот

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 
+0

но почему называется «get»? –

+0

Я попытался дать более подробное объяснение тому, что комментарий «setProperty вызывает соответствующий сеттер» я сделал ранее. Помогает ли это? – Keegan

+0

Ваши объяснения очень исчерпывающие. И теперь я знаю, почему «getProperty» не работает, и как заставить его работать правильно. Но я до сих пор не знаю, почему «получить» работу. В исходном коде MetaClassImpl я обнаружил, что при поиске свойства он выбирает метод «get» для выполнения этой задачи, но это не упоминается в документе. Это стандартное поведение? –