2013-05-08 2 views
8

In Kürze: 1. Ich habe eine letzte Klasse, die ich für einen dynamischen Proxy erstellen möchte. Wie kann ich es tun? 2. Kann ich MethodHandle in Method umwandeln?Wie kann ich einen dynamischen Proxy für die letzte Klasse erstellen?

Details Zuallererst existiert eine API, um MethodHandle in Method zu konvertieren? So etwas wie in java.lang.invoke.MethodHandles

public MethodHandle unreflect(Method m) throws IllegalAccessException; 

aber die entgegengesetzte Weise arrond?

Sagen wir, ich möchte dynamische java.lang.reflect.Method erstellen. Es wird defiend als

public final 
    class Method extends AccessibleObject implements GenericDeclaration, 
               Member ; 

Also, wenn ich Dynamische JDK-Proxy verwenden will, muß ich eine Schnittstelle (Mitglied zum Beispiel) verwenden. Es gibt jedoch 2 Haupt-Drawabacks. Als erstes Verfahren wie

public Class<?>[] getParameterTypes(); 

und wie

public Class<?> getReturnType(); 

ist nicht Teil einer Schnittstelle, während sie ausgiebig verwendet werden.

Der zweite Nachteil ist, dass es keinen Drop-In-Ersatz bietet. Das heißt, ich kann meinen dynamischen Proxy nicht an den Code übergeben, der java.lang.reflect.Method erwartet.

Ein anderer Ansatz ist die Verwendung von CGLIB oder Javaassist. AFAIK, CGLIB kann die letzte Klasse nicht vermitteln, oder? Kann Javaassist die letzte Klasse beenden? Wie kann ich den endgültigen Bezeichner aus der Klasse entfernen? AFAIL, Javvassist kann es irgendwie tun ...

Antwort

0

Sorry, was Sie wollen, ist nicht möglich:

Sie können CGLIB oder Javassist verwenden Proxies für konkrete Klassen zu erstellen, da diese Bibliotheken dynamisch eine Unterklasse der Klasse erzeugen du versuchst dich zu vertreten. Eine final Klasse kann nicht unterklassifiziert werden, daher können Sie auf diese Weise keinen Proxy erstellen.

PowerMock nicht lassen Sie Proxy final Klassen und Methoden, aber das ist, weil es Ihre Tests unter seiner speziellen ClassLoader verläuft die Javassist den Bytecode der Klassen zu modifizieren, verwendet Sie Proxy wollen, wie sie geladen sind werden. (Sie würden diese Art von Dingen in der Produktion nicht verwenden wollen, da im Allgemeinen die modifizierte "Zombie" - Version der Klasse, die Ergebnisse liefert, nicht gut für viel mehr ist, als einen spezifischen Schein - Komponententest auszuführen.)

Der PowerMock-Ansatz würde hier jedoch nicht funktionieren. Sie möchten java.lang.reflect.Method, das sich auf dem Bootstrap-Klassenpfad befindet, als Proxy verwenden und würden daher vor jedem PowerMock/Javassist-Tool geladen werden und daher nicht proxibar sein.

9

Es hängt davon ab, welche Art von Proxy Sie benötigen. Es gibt grundsätzlich drei Ansätze, wie Sie dies erreichen können, von denen zwei im Produktionscode möglich sind. Wie @probrekely festgestellt hat, ist das Problem von cglib oder javassist, dass sie dynamisch eine Unterklasse erstellen, was für finale Klassen nicht möglich ist. Sie können dies vermeiden durch:

  • Deaktivierung der Bytecode-Verifizierung. Die Java-Laufzeit überprüft Byte-Code, um sicherzustellen, dass kein bösartiger Byte-Code geladen wird.Dies ist wichtig, wenn Sie beispielsweise Klassen über das Netzwerk oder das Internet empfangen, z. B. ein Applet. Auf diese Weise können Sie eine Unterklasse einer letzten Klasse erstellen, da der Bytecode-Verifizierer Sie nicht stoppen würde. Hypothetisch können Sie diese Überprüfung deaktivieren, wenn Sie nur vertrauenswürdigen Code ausführen. Dies kann ausgeführt werden, indem:

    java -Xverify:none ApplicationName 
    

    Dies ist jedoch die Lösung, die ich Ihnen am wenigsten empfehlen würde. Ich würde diesen Ansatz nicht für Produktionscode verwenden, aber es ist sicherlich die einfachste zu implementierende Lösung.

  • Entfernen Sie den Modifikator final aus geladenen Klassen, entweder vor oder nach dem Laden der Klassen. Dies kann durch Verwendung eines Java agent erreicht werden. Ein Java-Agent kann beim Start der Anwendung über die Kommandozeile oder zur Laufzeit über die Attach API installiert werden. Mit einem Byte-Code-Tool wie ASM könnten Sie das ursprüngliche Byte-Array analysieren und den letzten Modifikator aus allen interessierenden Klassen entfernen. Es ist auch möglich, Klassen neu zu definieren, die bereits geladen wurden. Entfernen Sie einen final Modifikator führt keine Konflikte mit alten Klassenversionen, so dass eine solche Neudefinition immer möglich ist.

  • Gehen Sie wie zuvor beschrieben vor, indem Sie den Modifikator final entfernen, aber definieren Sie die geladene Klasse neu, um die gesamte Instrumentierungslogik in der ursprünglichen Klasse zu enthalten. Dieser Aporach wird sicherlich den größten Aufwand erfordern, aber dies wird Ihre Instrumentierung transperentiell zu allen anderen Codes machen. Dies wäre die sauberste Lösung aller Lösungen.

+0

könnten Sie die letzte Option näher erläutern? – piotrek

+0

Sie können Klassen mithilfe der Instrumentierungs-API neu definieren. Sagen wir, eine Klasse definiert eine Methode foo. Sie können die Methodenleiste umbenennen und von Ihrer neu definierten Methode foo aus aufrufen –

Verwandte Themen