Geschichte der Frage:
Proxy-Objekte und das Proxy-Pattern wurden in Java eingeführt, um Platzhalterobjekte zu erstellen, die den Zugriff auf echte Objekte kontrollieren, deren Verhalten ändern oder Querschnittsfunktionen (Sicherheit, Protokollierung, Metriken) einfügen können. Seit Java 1.3 gibt es Unterstützung für Standard-Dynamic-Proxys im Paket java.lang.reflect, gefolgt von beliebten Bibliotheken wie CGLIB und ByteBuddy.
Problem:
Es ist nicht immer möglich oder bequem, die Logik einer bestehenden Klasse direkt zu ändern. Oft ist es erforderlich, transparentes Verhalten hinzuzufügen (z. B. Protokollierung, Caching, Transaktionen), ohne den Quellcode des Objekts zu ändern, was mit den Mitteln der Standardvererbung nicht möglich ist.
Lösung:
Proxy-Objekte können statisch (durch manuelles Subclassing) und dynamisch (durch Reflexionsmechanismus oder Bytecode) implementiert werden. Ein dynamischer Proxy ermöglicht es, zur Laufzeit Objekte zu erstellen, die das benötigte Interface implementieren und Aufrufe an das echte Objekt innerhalb der Methode invoke() delegieren, wodurch Flexibilität für die Implementierung von externem Verhalten bereitgestellt wird.
Beispielcode:
import java.lang.reflect.*; interface Service { void doWork(); } class RealService implements Service { public void doWork() { System.out.println("Doing work!"); } } class LoggingInvocationHandler implements InvocationHandler { private final Object target; public LoggingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling: " + method.getName()); return method.invoke(target, args); } } Service real = new RealService(); Service proxy = (Service) Proxy.newProxyInstance( real.getClass().getClassLoader(), new Class[] {Service.class}, new LoggingInvocationHandler(real)); proxy.doWork(); // Der Aufruf wird protokolliert
Hauptmerkmale:
Kann ein standardmäßiger dynamischer Proxy in Java mit Klassen arbeiten, die keine Interfaces implementieren?
Nein. Der standardmäßige Proxy in Java arbeitet nur mit Interfaces. Für Klassen ohne Interfaces (z. B. wenn Sie eine normale Klasse proxy möchten) sind Drittanbieter-Tools erforderlich (z. B. CGLIB, ByteBuddy).
Kann ein Proxy für ein Interface mit privaten Methoden erstellt werden?
Nein. Interfaces in Java können keine privaten Methoden enthalten, die proxy erstellt werden müssen. Der dynamische Proxy implementiert die öffentlichen Methoden des Interfaces. Seit Java 9 gibt es private Default-Methoden, die aber nicht über einen Proxy zugänglich sind.
Was ist der Unterschied zwischen InvocationHandler und MethodInterceptor (CGLIB)?
InvocationHandler ist das Standardinterface der JDK, das für dynamische Proxys verwendet wird. Es empfängt Aufrufe von Methoden des Interfaces. MethodInterceptor ist das CGLIB-Interface, das es ermöglicht, Methodenaufrufe in beliebigen Klassen über dynamische Vererbung abzufangen. Der Unterschied liegt in der Anwendbarkeit und Ebenen: JDK arbeitet nur mit Interfaces, CGLIB mit beliebigen Klassen.
Ein Ingenieur wiederholt manuell den Protokollierungscode in jeder Methode aller Dienste. Bei der Hinzufügung neuer Methoden wird die Logik ständig dupliziert, wobei oft vergisst, die erforderlichen Aufrufe hinzuzufügen.
Vorteile:
Nachteile:
In das Projekt wird AOP über dynamischen Proxy integriert: Protokollierung und Transaktionskontrolle erfolgen über zentral implementierte Platzhalter-Wrapper.
Vorteile:
Nachteile: