Wie kann ich einen dynamischen Proxy für die letzte Klasse erstellen?

8

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

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

%Vor%

aber der umgekehrte Weg Arrond?

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

%Vor%

Also, wenn ich JDK Dynamic Proxy verwenden möchte, muss ich eine Schnittstelle verwenden (zB Member). Es gibt jedoch 2 Haupt-Drawabacks. Erstens, Methode wie

%Vor%

und wie

%Vor%

sind nicht Teil einer Schnittstelle, obwohl sie häufig verwendet werden.

Der zweite Nachteil besteht darin, dass kein Drop-In-Ersatz bereitgestellt werden kann. 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 ...

    
alexsmail 08.05.2013, 09:38
quelle

2 Antworten

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, indem Sie:

  • Deaktivierung der Bytecode-Überprüfung. 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 durch Ausführen von:

    erfolgen %Vor%

    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 final -Modifikator aus geladenen Klassen, entweder vor oder nach dem Laden der Klassen. Dies kann durch Verwendung eines Java-Agenten erreicht werden. Ein Java-Agent kann beim Start der Anwendung über die Befehlszeile oder zur Laufzeit über das installiert werden Fügen Sie die API hinzu . 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 nicht zu Konflikten mit alten Klassenversionen, so dass eine solche Neudefinition immer möglich ist.

  • Gehen Sie wie oben beschrieben vor, indem Sie den final -Modifizierer 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.

Rafael Winterhalter 01.12.2013 19:43
quelle
0

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

Sie können CGLIB oder Javassist verwenden, um Proxys für konkrete Klassen zu erstellen, da diese Bibliotheken dynamisch eine Unterklasse der Klasse generieren, die Sie proxigieren möchten. Eine final -Klasse kann nicht unterklassifiziert werden, daher können Sie auf diese Weise keinen Proxy erstellen.

PowerMock erlaubt es Ihnen, final Klassen und Methoden zu proxysen, aber das liegt daran, dass es Ihre Tests unter seiner speziellen ClassLoader ausführt, die Javassist verwendet, um den Bytecode der Klassen zu ändern, die beim Laden geladen werden sollen. (Sie würden diese Art von Dingen in der Produktion nicht verwenden wollen, da 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 stellvertretend für den Bootstrap-Klassenpfad angeben und würden daher vor jedem PowerMock / Javassist-Tool geladen werden und daher nicht proxibar sein.

    
pobrelkey 02.11.2013 00:15
quelle

Tags und Links