Java Dynamic Proxy: Was ist das und wie wird es verwendet?

Hallo allerseits!

Nun, vor dem neuen Jahr und dem Beginn des zehnten Streams scherzt der "Java Developer" völlig. Wir haben also eine offene Lektion, die wir für die Veröffentlichung vorbereiten, und die heutige Notiz, in der Sie mehr über den dynamischen Java-Proxy erfahren: Was es ist, wann und wie es im Code verwendet wird.

Was ist ein Proxy?

Ein Proxy ist ein Entwurfsmuster. Wir erstellen und verwenden es, um die Funktionalität vorhandener Klassen hinzuzufügen und zu ändern. In diesem Fall wird das Proxy-Objekt anstelle des ursprünglichen verwendet. Normalerweise wird dieselbe Methode wie beim Original verwendet, und in Java-Proxy-Klassen werden die ursprünglichen Methoden erweitert. Ein Proxy kann eine Methode für das Quellobjekt aufrufen, da es einen ursprünglichen Deskriptor hat.

So implementieren Proxy-Klassen bequem viele Dinge:

  • Protokollieren des Starts und Stopps der Methode;
  • zusätzliche Überprüfung der Argumente;
  • Nachahmung des Verhaltens der Quellklasse;
  • Implementierung einer verzögerten Initialisierung kostspieliger Ressourcen;



All dies geschieht, ohne den ursprünglichen Klassencode zu ändern. Die vollständige Liste ist nicht auf die obigen Beispiele beschränkt, sie sind nur ein kleiner Teil davon.

In der Praxis implementiert die Proxy-Klasse die Funktionalität nicht direkt. Nach dem Prinzip der alleinigen Verantwortung führt die Proxy-Klasse direkt nur Proxy durch, und Verhaltensänderungen werden in Handlern implementiert. Beim Aufrufen eines Proxy-Objekts anstelle des ursprünglichen Objekts entscheidet der Proxy, ob die ursprüngliche Methode oder einige Handler aufgerufen werden sollen. Der Handler kann sowohl seine eigene Aufgabe ausführen als auch auf die ursprüngliche Methode verweisen.

Obwohl das Proxy-Muster nicht nur zum Erstellen eines Proxy-Objekts und einer Proxy-Klasse zur Laufzeit verwendet wird, ist dies in Java ein besonders interessantes Thema. In diesem Artikel konzentriere ich mich auf solche Proxies.

Dies ist ein komplexes Thema, bei dem die Reflektionsklasse verwendet oder der Bytecode bearbeitet oder dynamisch generierter Java-Code kompiliert werden muss. Oder vielleicht auf einmal. Um zu verhindern, dass die neue Klasse zur Laufzeit als Bytecode verfügbar ist, müssen der generierte Bytecode und der Klassenladeprogramm den Bytecode laden. Verwenden Sie zum Erstellen von Bytecode cglib , bytebuddy oder den integrierten Java-Compiler.

In unserem Fall wird deutlich, wie wichtig die Aufgabentrennung ist. Sie müssen nur an Proxy-Klassen und die von ihnen aufgerufenen Handler denken. Eine Proxy-Klasse wird zur Laufzeit generiert, aber die von ihr aufgerufenen Handler können zum regulären Quellcode hinzugefügt und mit dem Rest des Programms kompiliert werden.

Wie verwende ich es in unserem Code?

Am einfachsten ist es, java.lang.reflect.Proxy zu verwenden, das Teil des JDK ist. Diese Klasse kann eine Proxy-Klasse oder deren Instanz direkt erstellen. Die Verwendung eines in Java integrierten Proxys ist sehr einfach. Sie müssen java.lang.InvocationHandler implementieren, damit das Proxy-Objekt es aufrufen kann. Die InvocationHandler Schnittstelle ist äußerst einfach und enthält nur eine Methode: invoke() . Beim Aufruf enthalten die Argumente das ursprüngliche Originalobjekt, die aufgerufene Methode (als Reflexion des Method ) und ein Array von Objekten der ursprünglichen Argumente. Das folgende Codefragment zeigt die Anwendung:

 package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkProxyDemo { interface If { void originalMethod(String s); } static class Original implements If { public void originalMethod(String s) { System.out.println(s); } } static class Handler implements InvocationHandler { private final If original; public Handler(If original) { this.original = original; } public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { System.out.println("BEFORE"); method.invoke(original, args); System.out.println("AFTER"); return null; } } public static void main(String[] args){ Original original = new Original(); Handler handler = new Handler(original); If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(), new Class[] { If.class }, handler); f.originalMethod("Hallo"); } } 

Um die ursprüngliche Methode des Quellobjekts aufzurufen, benötigt der Handler Zugriff darauf. Was wird von der Java-Proxy-Implementierung nicht bereitgestellt? Sie müssen das Argument selbst an die Handlerinstanz im Code übergeben. (Achten Sie auf das Objekt (normalerweise als Proxy bezeichnet), das als Argument an den aufgerufenen Handler übergeben wird. Dies ist ein Proxy-Objekt, das Java dynamisch generiert, und nicht das Objekt, das wir als Proxy verwenden möchten.) Sie können es daher als separates Objekt verwenden Handlerobjekte für jede Quellklasse sowie ein allgemeines Objekt, das weiß, wie das ursprüngliche Objekt aufgerufen wird, sofern überhaupt eine Methode dafür vorhanden ist.

Im Sonderfall können Sie einen Call-Handler und eine Proxy-Schnittstelle ohne das ursprüngliche Objekt erstellen. Darüber hinaus ist eine Klasse zum Implementieren einer Schnittstelle im Quellcode nicht erforderlich. Es wird von einer dynamisch erstellten Proxy-Klasse implementiert.

Wenn die Proxy-Klasse die Schnittstelle nicht implementiert, sollten Sie eine andere Proxy-Implementierung in Betracht ziehen.

DAS ENDE

Warten auf Ihre Kommentare und Fragen. Wie immer, entweder hier oder Sie können für einen Tag der offenen Tür nach Vitaly gehen.

Source: https://habr.com/ru/post/de434214/


All Articles