Historia pytania:
Obiekty proxy i wzorzec Proxy pojawiły się w Javie w celu tworzenia obiektów zastępczych, które mogą kontrolować dostęp do rzeczywistego obiektu, zmieniać jego zachowanie lub wprowadzać funkcje przekrojowe (bezpieczeństwo, logowanie, metryki). Począwszy od Javy 1.3, dodano wsparcie dla standardowych dynamicznych proxy w pakiecie java.lang.reflect, a później popularne biblioteki CGLIB i ByteBuddy.
Problem:
Nie zawsze jest możliwe/wygodne bezpośrednie zmienianie logiki istniejącej klasy. Często wymagana jest przezroczysta zmiana zachowania (np. logowanie, buforowanie, transakcje) bez zmiany kodu źródłowego obiektu, co jest niemożliwe za pomocą standardowego dziedziczenia.
Rozwiązanie:
Obiekty proxy mogą być realizowane statycznie (poprzez ręczne dziedziczenie) i dynamicznie (przez mechanizm refleksji lub bajtkodu). Dynamiczne proxy pozwala na tworzenie obiektów realizujących potrzebny interfejs i delegujących wywołania do rzeczywistego obiektu wewnątrz metody invoke(), co daje elastyczność do wprowadzania zewnętrznego zachowania.
Przykład kodu:
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(); // Logowane wywołanie
Kluczowe cechy:
Czy standardowe dynamiczne Proxy w Javie może działać z klasami, które nie implementują interfejsów?
Nie. Standardowe Proxy w Javie działa tylko z interfejsami. Dla klas bez interfejsów (na przykład, jeśli chcesz zproxyować zwykłą klasę) potrzebne są zewnętrzne środki (np. CGLIB, ByteBuddy).
Czy może być stworzony proxy dla interfejsu z prywatnymi metodami?
Nie. Interfejsy w Javie nie mogą zawierać prywatnych metod, które byłyby wymagane do proxy. Dynamiczne proxy implementuje publiczne metody interfejsu. Począwszy od Javy 9 pojawiły się prywatne metody domyślne, ale nie są one dostępne przez proxy.
Jaka jest różnica między InvocationHandler a MethodInterceptor (CGLIB)?
InvocationHandler — standardowy interfejs JDK, używany do dynamicznych proxy. Przyjmuje wywołania metod interfejsu. MethodInterceptor — interfejs CGLIB, pozwalający przechwytywać wywołania metod w dowolnych klasach poprzez dynamiczne dziedziczenie. Różnica polega na zastosowaniu i poziomie: JDK działa tylko z interfejsami, CGLIB — z dowolnymi klasami.
Inżynier ręcznie powtarza kod logowania w każdej metodzie wszystkich usług. Przy dodawaniu nowych metod ciągle dubluje logikę, często zapominając dodać potrzebne wywołania.
Plusy:
Minusy:
W projekcie wdrożono AOP poprzez dynamiczne proxy: logowanie i kontrola transakcji realizowane są przez centralnie implementowane opakowania-zastępniki.
Plusy:
Minusy: