2017-02-20 2 views
2

Wir begannen mit einem Glas mit einer Klasse mit einer Methode, wie: das Ergebnis dieser Methode nutzlos war (in der Tat, immer wahr) und Kundennur Ergebnis Methode ändern und Klassenlader NoSuchMethod

boolean foo(int bar) { ... } 

jedoch, dass nutzte dieses Ergebnis für alles, was in einem Fehler fehlschlägt. Aus diesem Grund wurde die Methode geändert in:

void foo(int bar) { ... } 

und alle Elemente neu kompiliert. So können wir alle Benutzer davon ausgehen, dieses Glas die Methode nennt als:

foo(14); 

keine verwendet die Form (und ist für diese Frage außerhalb des Bereichs, wenn es jemanden gibt):

boolean x = foo(14); 

Angenommen Kein Client, neu oder älter, verwendet das boolesche Ergebnis.

Das Problem war, wenn in Zielsystemen, das neue Glas geladen wird, ohne die Clients zu aktualisieren. Nicht aktualisierte Clients schlagen mit einer Ausnahme "NoSuchMethod" fehl, wenn nach der "foo" -Methode mit dem Ergebnis "boolean" gesucht wird. Das ist:

  • Client hat eine Anweisung wie "foo (14);" ohne Verwendung der Methode Ergebnis
  • Client ist mit dem Jar mit Boolean-Methode kompiliert.
  • Client und Bibliothek wird in Zielsystem geladen
  • Bibliothek mit neuem Glas mit void-Methode
  • Client Abstürze mit „NoSuchMethod“
scheint

Der Ursprung des Problems aktualisiert wird, dass sowohl zu sein, Java und C/C++ erlaubt keine zwei Methoden, die sich nur im Ergebnis unterscheiden, aber nur Java "name mangling" enthält den Ergebnistyp in dem Namen, den der Klassenlader (Linker in C/C++) sucht.

Die Frage ist: Es ist möglich, in irgendeiner Weise das Bibliotheksjar oder den Klassenlader zu überlisten, um "NoSuchMethod" Ausnahme in diesem Szenario zu überspringen?

Antwort

1

Nur meine Gedanken.

Sie können versuchen, den Bytecode einer Klasse nach der tatsächlichen Kompilierung mit special tools als Hotfix zu ändern. Im Bytecode können solche Methoden koexistieren.

3

Dieses Verhalten wird vollständig erwartet. Dies liegt daran, dass die kompilierten Klassen des Clients constant pool enthalten, einschließlich symbolische Referenz für eine Methode.

Nehmen wir an, wir Klasse haben FooClass mit zwei Methoden:

public boolean foo1(int bar) { 
    return true; 
} 

public void foo2(int bar) { 
    // ... 
} 

Und nehmen wir an, wir haben eine andere Klasse Main, wo wir beide Methoden aufrufen:

FooClass fc = new FooClass(); 
    fc.foo1(1); 
    fc.foo2(1); 

Wenn wir Main.class zerlegen wir werden sehen, Diese Methode Referenzen unterscheiden sich nicht nur in ihrem Namen, aber in zurück Typ (bemerkt (I) Z und (I) V):

 7: astore_1 
    8: aload_1 
    9: iconst_1 
    10: invokevirtual #4     // Method q42340444/FooClass.foo1:(I)Z 
    13: pop 
    14: aload_1 
    15: iconst_1 
    16: invokevirtual #5     // Method q42340444/FooClass.foo2:(I)V 

Wo konstant Pool enthält:

#4 = Methodref   #2.#25   // q42340444/FooClass.foo1:(I)Z 
    #5 = Methodref   #2.#26   // q42340444/FooClass.foo2:(I)V 

Mit anderen Worten, es ursprüngliche Methode der Rückgabetyp bewahrt und sieht in verknüpften Bibliotheken für genau die gleiche Methode, die NoSuchMethod Ausnahme in Ihrem Fall verursacht.

Genauer gesagt sind kompilierte Benutzerklassen mit der Methode FooClass.foo:(I)Z verknüpft, während Ihre neue Bibliothek keine Methode mit einer solchen Beschreibung enthält, sondern nur FooClass.foo:(I)V.

Fazit: Sie können dieses Problem nicht lösen, ohne die alte Methode Unterschrift zu halten (kann es besser ist, diese Methode mit @Deprecated Anmerkung Anmerkungen versehen) oder durch Kunden Code neu kompilieren. Ja

+0

, es ist, wie Sie beschreiben, aber Ihr Vorschlag der aktuelle eigentliche Problem ist zu lösen? –

+0

Sie können es nicht lösen, ohne die alte Methodensignatur beizubehalten oder den Clientcode neu zu kompilieren. – Andremoniy

+0

Leider kann ich ältere Methode und neuere nicht "behalten", weil Java zwei Methoden nicht zulässt, die sich nur im Ergebnistyp unterscheiden. –

Verwandte Themen