Proxy Tasarım Deseni durumunda, JDK'nın Dinamik Proxy'si ile CGLib gibi üçüncü taraf dinamik kod oluşturma API'leri arasındaki fark nedir ?
Her iki yaklaşımı da kullanmak arasındaki fark nedir ve ne zaman biri diğerini tercih etmelidir?
Proxy Tasarım Deseni durumunda, JDK'nın Dinamik Proxy'si ile CGLib gibi üçüncü taraf dinamik kod oluşturma API'leri arasındaki fark nedir ?
Her iki yaklaşımı da kullanmak arasındaki fark nedir ve ne zaman biri diğerini tercih etmelidir?
Yanıtlar:
JDK Dinamik proxy yalnızca arabirime göre proxy yapabilir (bu nedenle hedef sınıfınızın, daha sonra proxy sınıfı tarafından uygulanan bir arabirim uygulaması gerekir).
CGLIB (ve javassist) alt sınıflandırma yoluyla bir proxy oluşturabilir. Bu senaryoda, proxy hedef sınıfın bir alt sınıfı olur. Arayüzlere gerek yok.
Java Dynamic proxy'leri proxy yapabilir: public class Foo implements iFoo
burada CGLIB proxy yapabilir:public class Foo
DÜZENLE:
Javassist ve CGLIB'nin alt sınıflandırma yoluyla proxy kullandıkları için, buna dayanarak çerçeveler kullanırken son yöntemleri bildirememenizin veya sınıfı final yapamamanızın nedeni olduğunu belirtmeliyim. Bu, bu kütüphanelerin sınıfınızı alt sınıflara ayırmasına ve yöntemlerinizi geçersiz kılmasına izin vermez.
İşlev farklılıkları
JDK proxy'leri, alt sınıflandırma sırasında herhangi bir arabirim kümesinin uygulanmasına izin verir Object
. Herhangi bir arabirim yöntemi ve Object::hashCode
, Object::equals
ve Object::toString
daha sonra bir iletilir InvocationHandler
. Ayrıca, standart kitaplık arabirimi java.lang.reflect.Proxy
uygulanır.
cglib, herhangi bir son sınıf olmayan sınıfın alt sınıfını yaparken herhangi bir arabirim kümesini uygulamanıza olanak tanır. Ayrıca, yöntemler isteğe bağlı olarak geçersiz kılınabilir, yani soyut olmayan tüm yöntemlerin ele geçirilmesi gerekmez. Ayrıca, bir yöntemi uygulamanın farklı yolları vardır. Ayrıca bir InvocationHandler
sınıf (farklı bir pakette) sunar, ancak a gibi daha gelişmiş önleyicileri kullanarak süper yöntemleri çağırmaya da izin verir MethodInterceptor
. Ayrıca, cglib gibi özel müdahalelerle performansı artırabilir FixedValue
. Bir keresinde cglib için farklı önleyicilerin bir özetini yazdım .
Performans farklılıkları
JDK vekil sunucuları sadece tek bir müdahale görevlisi ile oldukça naif bir şekilde uygulanır InvocationHandler
. Bu, her zaman satır içine alınamayan bir uygulamaya sanal yöntem gönderilmesini gerektirir. Cglib bazen performansı artırabilecek özel bayt kodu oluşturmaya izin verir. 18 saplama yöntemiyle bir arayüz uygulamak için bazı karşılaştırmalar:
cglib JDK proxy
creation 804.000 (1.899) 973.650 (1.624)
invocation 0.002 (0.000) 0.005 (0.000)
Zaman, parantezlerde standart sapma ile nanosaniye cinsinden not edilir. Bente Buddy'nin cglib'e daha modern bir alternatif olduğu Byte Buddy'nin eğitiminde karşılaştırmalı değerlendirme hakkında daha fazla ayrıntı bulabilirsiniz . Ayrıca, cglib'in artık aktif geliştirme altında olmadığını unutmayın.
Dinamik proxy: JDK Reflection API'sı kullanılarak çalışma zamanında arabirimlerin dinamik uygulamaları .
Örnek: Spring, işlemler için aşağıdaki gibi dinamik proxy'ler kullanır:
Oluşturulan vekil fasulye üstüne gelir. Fasulyeye ulusötesi davranış ekler. Burada proxy JDK Reflection API kullanarak çalışma zamanında dinamik olarak üretir.
Bir uygulama durdurulduğunda, proxy yok edilir ve dosya sisteminde yalnızca arayüz ve fasulye bulunur.
Yukarıdaki örnekte arayüz var. Ancak arayüzün uygulanmasında çoğu iyi değildir. Yani fasulye bir arayüz uygulamıyor, bu durumda kalıtım kullanıyoruz:
Bu tür vekil sunucuları oluşturmak için Spring, CGLib adında bir üçüncü taraf kütüphanesi kullanır .
CGLib ( K ode G eneration Lib rary ) ASM üzerine inşa edilmiştir , bu temel olarak proxy genişletme fasulye oluşturmak için kullanılır ve proxy yöntemlerinde fasulye davranışı ekler.
Spring AOP, belirli bir hedef nesne için proxy oluşturmak üzere JDK dinamik proxy'lerini veya CGLIB'yi kullanır. (JDK dinamik proxy'leri tercihiniz olduğunda tercih edilir).
Yakınlaştırılacak hedef nesne en az bir arabirim uygularsa, bir JDK dinamik proxy'si kullanılır. Hedef tür tarafından uygulanan tüm arabirimler proxy üzerinden oluşturulacaktır. Hedef nesne herhangi bir arabirim uygulamazsa bir CGLIB proxy'si oluşturulur.
CGLIB proxy'nin kullanımını zorlamak istiyorsanız (örneğin, yalnızca arabirimleri tarafından uygulananları değil, hedef nesne için tanımlanan her yöntemi proxy yapmak için) bunu yapabilirsiniz. Bununla birlikte, dikkate alınması gereken bazı hususlar vardır:
geçersiz kılınamayacakları için nihai yöntemler önerilemez.
Sınıf yolunuzda CGLIB 2 ikili dosyalarına ihtiyacınız olurken JDK ile dinamik proxy'ler kullanılabilir. Bahar, CGLIB gerektiğinde sizi otomatik olarak uyarır ve CGLIB kütüphane sınıfları sınıf yolunda bulunmaz.
Proxy nesnenizin kurucusu iki kez çağrılır. Bu, her proxy nesnesi için bir alt sınıfın oluşturulduğu CGLIB proxy modelinin doğal bir sonucudur. Her proxy örneği için iki nesne oluşturulur: gerçek proxy nesne ve öneriyi uygulayan alt sınıf örneği. JDK proxy'leri kullanılırken bu davranış gösterilmez. Genellikle, proxy türünün yapıcısını iki kez çağırmak bir sorun değildir, çünkü genellikle sadece atamalar yapılır ve kurucuya gerçek bir mantık uygulanmaz.