Historia pytania:
Dynamic Proxy Pattern pojawił się w Javie 1.3 i pozwolił na realizację wzorców AOP, logowania, bezpieczeństwa, dynamicznego generowania kodu bez ręcznego pisania klas proxy. Stało się to bardzo popularne w Spring, Hibernate i innych frameworkach.
Problem:
Czasami konieczne jest dodanie funkcjonalności do już istniejącego interfejsu lub zmiana zachowania metod w czasie wykonania, bez zmiany kodu źródłowego. Przykład: logowanie każdego wywołania metody, sprawdzanie dostępu, profilowanie. Głównym problemem jest niemożność zrobienia tego ręcznie dla wszystkich klas, zwłaszcza gdy klas jest dużo lub są one generowane dynamicznie.
Rozwiązanie:
Java dostarcza standardowe API w pakiecie java.lang.reflect do tworzenia obiektów proxy "w locie" dla dowolnych interfejsów za pomocą Proxy.newProxyInstance. Realizuje się obiekt InvocationHandler, który obsługuje każde wywołanie metody i implementuje swoją logikę.
Przykład kodu:
import java.lang.reflect.*; interface Service { void serve(); } class ServiceImpl implements Service { public void serve() { System.out.println("Wykonywanie..."); } } class LoggingHandler implements InvocationHandler { private final Object target; public LoggingHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Wywołanie " + method.getName()); return method.invoke(target, args); } } Service service = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new LoggingHandler(new ServiceImpl())); service.serve();
Kluczowe cechy:
Czy można za pomocą standardnego dynamic proxy "owinąć" zwykłą klasę, która nie implementuje interfejsów?
Nie. Dynamic Proxy działa tylko z interfejsami. Dla klas potrzebna jest manipulacja bajtami (na przykład CGLIB).
Co się stanie, jeśli interfejs zawiera metody z niezgodnym typem zwracanym z InvocationHandler?
Zostanie rzucony wyjątek ClassCastException, dlatego wartość zwracana musi być ściśle zgodna z definicją interfejsu.
Czy można używać dynamic proxy dla metod finalnych?
Nie. Proxy działa tylko na poziomie interfejsów, a w interfejsach nie ma metod finalnych. Metody finalne nie mogą być w ogóle przesłonięte.
Negatywny przypadek
Programista próbował wdrożyć logowanie audytów przez Proxy na klasach bez interfejsu
Zalety:
Wady:
Pozytywny przypadek
Wydzielono kontrakt w postaci interfejsu, już pod niego zrobiono opakowanie proxy
Zalety:
Wady: