W przypadku wzorca projektowego proxy , jaka jest różnica między dynamicznym proxy JDK a interfejsami API do generowania kodu dynamicznego innych firm, takimi jak CGLib ?
Jaka jest różnica między stosowaniem obu podejść i kiedy należy je preferować?
W przypadku wzorca projektowego proxy , jaka jest różnica między dynamicznym proxy JDK a interfejsami API do generowania kodu dynamicznego innych firm, takimi jak CGLib ?
Jaka jest różnica między stosowaniem obu podejść i kiedy należy je preferować?
Odpowiedzi:
JDK Dynamiczne proxy może proxy tylko przez interfejs (więc Twoja klasa docelowa musi zaimplementować interfejs, który jest następnie implementowany przez klasę proxy).
CGLIB (i javassist) mogą tworzyć proxy przez tworzenie podklas. W tym scenariuszu proxy staje się podklasą klasy docelowej. Nie potrzeba interfejsów.
Więc dynamiczne proxy Java mogą proxy: public class Foo implements iFoogdzie CGLIB może proxy:public class Foo
EDYTOWAĆ:
Powinienem o tym wspomnieć, ponieważ javassist i CGLIB używają proxy przez podklasy, że jest to powód, dla którego nie można zadeklarować ostatecznych metod ani uczynić klasy ostateczną, gdy używa się frameworków, które na tym polegają. To powstrzymałoby te biblioteki przed zezwalaniem na podklasy twojej klasy i nadpisywanie twoich metod.
Różnice w funkcjonalności
Serwery proxy JDK pozwalają na implementację dowolnego zestawu interfejsów podczas tworzenia podklas Object. Dowolna metoda interfejsu plus Object::hashCode, Object::equalsa Object::toStringnastępnie jest przekazywana do pliku InvocationHandler. Dodatkowo java.lang.reflect.Proxyzaimplementowano standardowy interfejs biblioteki .
cglib umożliwia zaimplementowanie dowolnego zestawu interfejsów podczas tworzenia podklas dowolnej klasy, która nie jest ostateczna. Ponadto metody można opcjonalnie przesłonić, tj. Nie wszystkie metody nieabstrakcyjne muszą zostać przechwycone. Ponadto istnieją różne sposoby implementacji metody. Oferuje również InvocationHandlerklasę (w innym pakiecie), ale pozwala również wywoływać super metody przy użyciu bardziej zaawansowanych przechwytywaczy, na przykład a MethodInterceptor. Ponadto cglib może poprawić wydajność dzięki wyspecjalizowanym przechwyceniom, takim jak FixedValue. Kiedyś napisałem podsumowanie różnych przechwytywaczy dla cglib .
Różnice w wydajności
Serwery proxy JDK są implementowane raczej naiwnie z tylko jednym dyspozytorem przechwytywania, plikiem InvocationHandler. Wymaga to wysłania metody wirtualnej do implementacji, która nie zawsze może być wbudowana. Cglib pozwala na tworzenie wyspecjalizowanego kodu bajtowego, co czasami może poprawić wydajność. Oto kilka porównań implementacji interfejsu z 18 metodami pośredniczącymi:
cglib JDK proxy
creation 804.000 (1.899) 973.650 (1.624)
invocation 0.002 (0.000) 0.005 (0.000)
Czas jest podawany w nanosekundach z odchyleniem standardowym w szelkach. Więcej szczegółów na temat testu porównawczego można znaleźć w samouczku Byte Buddy, w którym Byte Buddy jest bardziej nowoczesną alternatywą dla cglib. Należy również zauważyć, że cglib nie jest już aktywnie rozwijany.
Dynamiczne proxy: dynamiczne implementacje interfejsów w czasie wykonywania przy użyciu JDK Reflection API .
Przykład: Spring używa dynamicznych proxy do transakcji w następujący sposób:
Wygenerowany proxy znajduje się na szczycie fasoli. Dodaje fasoli do międzynarodowego zachowania. Tutaj proxy generuje się dynamicznie w czasie wykonywania przy użyciu JDK Reflection API.
Kiedy aplikacja zostanie zatrzymana, proxy zostanie zniszczone, a my będziemy mieć tylko interfejs i bean w systemie plików.
W powyższym przykładzie mamy interfejs. Jednak w większości implementacji interfejs nie jest najlepszy. Więc bean nie implementuje interfejsu, w takim przypadku używamy dziedziczenia:
Aby wygenerować takie proxy, Spring używa biblioteki innej firmy o nazwie CGLib .
CGLib ( C ode G eneration Lib rary ) jest zbudowany na bazie ASM , jest używany głównie do generowania komponentu bean rozszerzającego proxy i dodaje zachowanie fasoli w metodach proxy.
Spring AOP używa dynamicznych proxy JDK lub CGLIB do tworzenia proxy dla danego obiektu docelowego. (Dynamiczne proxy JDK są preferowane, gdy masz wybór).
Jeśli obiekt docelowy, który ma być proxy, implementuje co najmniej jeden interfejs, zostanie użyty dynamiczny serwer proxy JDK. Wszystkie interfejsy zaimplementowane przez typ docelowy będą proxy. Jeśli obiekt docelowy nie implementuje żadnych interfejsów, zostanie utworzony serwer proxy CGLIB.
Jeśli chcesz wymusić użycie proxy CGLIB (na przykład do proxy każdej metody zdefiniowanej dla obiektu docelowego, a nie tylko tych zaimplementowanych przez jego interfejsy), możesz to zrobić. Należy jednak wziąć pod uwagę kilka kwestii:
nie można zalecić ostatecznych metod, ponieważ nie można ich zastąpić.
Będziesz potrzebował plików binarnych CGLIB 2 w swojej ścieżce klas, podczas gdy dynamiczne proxy są dostępne z JDK. Spring automatycznie ostrzeże Cię, gdy potrzebuje CGLIB, a klasy biblioteki CGLIB nie zostaną znalezione w ścieżce klas.
Konstruktor twojego obiektu proxy zostanie wywołany dwukrotnie. Jest to naturalna konsekwencja modelu proxy CGLIB, w którym podklasa jest generowana dla każdego obiektu proxy. Dla każdej instancji proxy tworzone są dwa obiekty: rzeczywisty obiekt proxy i instancja podklasy, która implementuje poradę. To zachowanie nie jest widoczne podczas korzystania z serwerów proxy JDK. Zwykle dwukrotne wywołanie konstruktora typu proxy nie jest problemem, ponieważ zwykle mają miejsce tylko przypisania i nie ma rzeczywistej logiki w konstruktorze.