2016-01-21 58 views
5

Zrobiłem to, robiąc refaktor. Połączenia z getProperties() powodowały wzrost wykorzystania procesora. Odkryliśmy, że jeśli posiadasz getter bez powiązanego atrybutu, wywołanie getProperties(), które pobiera, jest wywoływane ponad 1000 razy. Rozwiązanie/obejście jest oczywiste i wiemy, że ma to coś wspólnego z metaprogramowaniem, ale dlaczego tak się dzieje (jaki punkt w tym groźnym źródle)? Zobacz kod skryptu groovy poniżej:Groovy getProperties() wywołanie wywołujące getter dla nieistniejącego atrybutu ponad 1000 razy

class tester { 

    int count = 0 

    public getVar() { 
     println count++ + " getVar() called!" 
     return var 
    } 
} 

def t = new tester() 

t.getProperties() 

println "done!" 

Powinieneś zobaczyć getVar() wywołany ponad 1000 razy. 1068, aby być dokładnym dla nas.

+1

Dość dziwne. Po prostu wypróbowałem to w niesamowitej konsoli internetowej http://groovyconsole.appspot.com/ i widzę ją wykonaną 110 razy. –

+9

Dzieje się tak z powodu tego wiersza 'return var'. To faktycznie wywołuje samą 'getVar()' w rekursji, ponieważ 'return var' jest takie samo jak wywołanie' return getVar() '. Drukuje liczbę do momentu przepełnienia stosu. – dmahapatro

+2

'return var' jest problemem. –

Odpowiedz

0

Na pytanie prawdopodobnie już udzielono odpowiedzi w komentarzach, ale jeszcze bardziej się zagłębiłem, aby odpowiedzieć również na pytanie "jaki punkt w tym groźnym źródle".

Po wywołaniu getProperties() na wystąpienie tester Groovy zrobi jej magii i wreszcie wywołać DefaultGroovyMethods#getProperties(Object) który (w Groovy 2.4.7) wygląda tak:

public static Map getProperties(Object self) { 
    List<PropertyValue> metaProps = getMetaPropertyValues(self); // 1 
    Map<String, Object> props = new LinkedHashMap<String, Object>(metaProps.size()); 

    for (PropertyValue mp : metaProps) { 
     try { 
      props.put(mp.getName(), mp.getValue()); // 2 
     } catch (Exception e) { 
      LOG.throwing(self.getClass().getName(), "getProperty(" + mp.getName() + ")", e); 
     } 
    } 
    return props; 
} 

pierwsze, Groovy określa meta właściwości dany obiekt (patrz 1). To powrotu trzy właściwości:

  • var: getter tylko (getVar()) nie ustawiająca, ma pola
  • class: getter tylko (odziedziczone Object) nie ustawiająca, ma pola
  • count: gettera seter (oba generowane przez Groovy) i pole

Możesz to łatwo zweryfikować, dzwoniąc pod numer t.getMetaPropertyValues().

Następnie Groovy próbuje pobrać aktualną wartość każdej właściwości i umieścić ją na mapie (patrz 2). Po osiągnięciu var, pamięta, że ​​var ma getter (mianowicie getVar()) i wywołuje go. getVar() jednak ponownie zwraca var. W przypadku Groovy jest to dokładnie taka sama właściwość, jak określono w pierwszym kroku. Po raz kolejny nazywa swój getter getVar() i zaczyna się nieskończona pętla.

W pewnym momencie, w zależności od JVM, skutkuje to StackOverflowError, który jest dokładnie to, co ta strona jest o :-D