Java Reflection – Dynamic Proxies

Mit Java Reflection erstellen Sie dynamische Implementierungen von Schnittstellen zur Laufzeit. Sie tun dies mit der Klasse java.lang.reflect.Proxy . Der Name dieser Klasse ist der Grund, warum ich diese dynamischen Schnittstellenimplementierungen als dynamische Proxys bezeichne. Dynamische Proxys können für viele verschiedene Zwecke verwendet werden, z. B. Datenbankverbindungs- und Transaktionsmanagement, dynamische Scheinobjekte für Komponententests und andere AOP-ähnliche Methodenabfangzwecke.

Erstellen von Proxys

Sie erstellen dynamische Proxys mit der Methode Proxy.newProxyInstance(). Die newProxyInstance() -Methoden benötigen 3 Parameter:

  1. Die ClassLoader, die die dynamische Proxy-Klasse “laden” soll.
  2. Ein Array von zu implementierenden Schnittstellen.
  3. Und InvocationHandler, an die alle Methodenaufrufe des Proxys weitergeleitet werden sollen.

Hier ist ein Beispiel:

Nach dem Ausführen dieses Codes enthält die Variable proxy eine dynamische Implementierung der Schnittstelle MyInterface. Alle Aufrufe des Proxys werden an die handler -Implementierung der allgemeinen InvocationHandler -Schnittstelle weitergeleitet. InvocationHandler’s werden im nächsten Abschnitt behandelt.

InvocationHandler’s

Wie bereits erwähnt, müssen Sie eine InvocationHandler Implementierung an die Proxy.newProxyInstance() Methode übergeben. Alle Methodenaufrufe an den dynamischen Proxy werden an diese InvocationHandler -Implementierung weitergeleitet. So sieht die InvocationHandler -Schnittstelle aus:

public interface InvocationHandler{ Object invoke(Object proxy, Method method, Object args) throws Throwable;}

Hier ist eine Beispielimplementierung:

public class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object args) throws Throwable { //do something "dynamic" }}

Der Parameter proxy, der an die Methode invoke() übergeben wird, ist das dynamische Proxy-Objekt, das die Schnittstelle implementiert. Meistens brauchen Sie dieses Objekt nicht.

Das Method -Objekt, das an die invoke() -Methode übergeben wird, stellt die Methode dar, die auf der Schnittstelle aufgerufen wird, die der dynamische Proxy implementiert. Aus dem Method -Objekt können Sie den Methodennamen, Parametertypen, Rückgabetyp usw. abrufen. Weitere Informationen finden Sie im Text zu Methoden.

Das Array Object args enthält die Parameterwerte, die beim Aufruf der Methode in der implementierten Schnittstelle an den Proxy übergeben wurden. Hinweis: Primitive (int, long usw.) in der implementierten Schnittstelle werden in ihre Objektgegenstücke (Integer, Long usw.) eingeschlossen.).

Bekannte Anwendungsfälle

Es ist bekannt, dass dynamische Proxys für mindestens die folgenden Zwecke verwendet werden:

  • Datenbankverbindungs- und Transaktionsmanagement
  • Dynamische Mock-Objekte für Unit-Tests
  • Anpassung des DI-Containers an benutzerdefinierte Factory-Schnittstellen
  • AOP-ähnliches Methodenabfangen

Datenbankverbindungs- und Transaktionsmanagement

Das Spring Framework verfügt über einen Transaktionsproxy, der eine Transaktion für Sie starten und festschreiben / zurücksetzen kann. Wie das funktioniert, ist im Text Advanced Connection and Transaction Demarcation and Propagation genauer beschrieben , daher werde ich es nur kurz beschreiben. Die Aufrufsequenz wird ungefähr so:

Dynamische Mock-Objekte für Unit-Tests

Die Butterfly Testing Tools verwenden dynamische Proxys, um dynamische Stubs, Mocks und Proxys für Unit-Tests zu implementieren. Wenn Sie eine Klasse A testen, die eine andere Klasse B verwendet (Schnittstelle wirklich), können Sie eine Scheinimplementierung von B anstelle eines echten B an A übergeben. Alle Methodenaufrufe von B werden jetzt aufgezeichnet, und Sie können festlegen, welche Rückgabewerte der Mock B zurückgeben soll.

Darüber hinaus können Sie mit Butterfly-Testwerkzeugen ein echtes B in ein Mock-B einbinden, sodass alle Methodenaufrufe des Mocks aufgezeichnet und dann an das echte B weitergeleitet werden. Der DAO sieht den Unterschied nicht und der DAO kann wie gewohnt Daten in die Datenbank lesen / schreiben, da der Mock alle Aufrufe an die Datenbank weiterleitet. Aber jetzt können Sie über den Mock überprüfen, ob das DAO die Verbindung richtig verwendet, zum Beispiel, wenn connection.close() aufgerufen wird (oder NICHT aufgerufen wird), wenn Sie das erwartet haben. Dies ist normalerweise nicht möglich, aus dem Rückgabewert eines DAO zu bestimmen.

Anpassung des DI-Containers an benutzerdefinierte Factory-Schnittstellen

Der Dependency Injection Container Butterfly Container verfügt über eine leistungsstarke Funktion, mit der Sie den gesamten Container in von ihm erzeugte Beans injizieren können. Da Sie jedoch keine Abhängigkeit von der Container-Schnittstelle wünschen, kann sich der Container an eine benutzerdefinierte Factory-Schnittstelle Ihres Designs anpassen. Sie benötigen nur die Schnittstelle. Keine Implementierung. Daher könnten die Factory-Schnittstelle und Ihre Klasse ungefähr so aussehen:

public interface IMyFactory { Bean bean1(); Person person(); ...}

Wenn die MyAction -Klasse Methoden für die IMyFactory -Instanz aufruft, die vom Container in seinen Konstruktor injiziert wird, werden die Methodenaufrufe in Aufrufe der IContainer.instance() -Methode übersetzt, mit der Sie Instanzen aus dem Container abrufen. Auf diese Weise kann ein Objekt den Container zur Laufzeit als Factory verwenden, anstatt nur Abhängigkeiten zur Erstellungszeit in sich selbst einzufügen. Und dies ohne Abhängigkeiten von anderen containerspezifischen Schnittstellen.

AOP-ähnliches Abfangen von Methoden

Das Spring-Framework ermöglicht das Abfangen von Methodenaufrufen an eine bestimmte Bean, vorausgesetzt, die Bean implementiert eine Schnittstelle. Das Spring Framework wickelt die Bean in einen dynamischen Proxy ein. Alle Aufrufe der Bean werden dann vom Proxy abgefangen. Der Proxy kann entscheiden, andere Methoden für andere Objekte entweder vor, anstelle oder nach dem Delegieren des Methodenaufrufs an das Bean-Objekt aufzurufen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.