[英]What is the difference between JDK dynamic proxy and CGLib?
JDK動態代理只能通過接口代理(因此您的目標類需要實現一個接口,然后由代理類實現)。
CGLIB(和javassist)可以通過子類創建代理。 在這種情況下,代理成為目標類的子類。 不需要接口。
所以Java動態代理可以代理: public class Foo implements iFoo
CGLIB可以代理的public class Foo implements iFoo
: public class Foo
編輯:
我應該提一下,因為javassist和CGLIB通過子類化使用代理,這就是你在使用依賴於它的框架時不能聲明最終方法或使類最終的原因。 這將阻止這些庫允許子類化您的類並覆蓋您的方法。
功能上的差異
JDK代理允許在子類化Object
實現任何接口集。 然后將任何接口方法加上Object::hashCode
, Object::equals
和Object::toString
轉發到InvocationHandler
。 此外,還實現了標准庫接口java.lang.reflect.Proxy
。
cglib允許您在子類化任何非final類時實現任何接口集。 此外,可以任選地覆蓋方法,即不需要攔截所有非抽象方法。 此外,有不同的方法來實現方法。 它還提供了一個InvocationHandler
類(在不同的包中),但它也允許通過使用更高級的攔截器來調用超級方法,例如MethodInterceptor
。 此外,cglib可以通過像FixedValue
這樣的專門攔截來提高性能。 我曾經為cglib寫過不同攔截器的摘要 。
性能差異
只使用一個攔截調度程序InvocationHandler
,JDK代理實現得相當天真。 這需要對實現進行虛擬方法調度,而實現並不總是內聯。 Cglib允許創建專門的字節代碼,有時可以提高性能。 以下是使用18個存根方法實現接口的一些比較:
cglib JDK proxy
creation 804.000 (1.899) 973.650 (1.624)
invocation 0.002 (0.000) 0.005 (0.000)
時間以納秒為單位,括號中標准偏差。 您可以在Byte Buddy的教程中找到有關基准測試的更多詳細信息,其中Byte Buddy是cglib的更現代的替代品。 另請注意,cglib不再處於活動開發階段。
動態代理:使用JDK Reflection API在運行時動態實現接口。
示例: Spring使用事務的動態代理,如下所示:
生成的代理位於bean之上。 它為bean添加了跨國行為。 這里代理使用JDK Reflection API在運行時動態生成。
當應用程序停止時,代理將被銷毀,我們將只在文件系統上有接口和bean。
在上面的例子中我們有接口。 但在大多數接口的實現並不是最好的。 所以bean沒有實現接口,在這種情況下我們使用繼承:
為了生成這樣的代理,Spring使用名為CGLib的第三方庫。
CGLib( C ode G eneration Lib rary)建立在ASM之上,這主要用於生成代理擴展bean並在代理方法中添加bean行為。
Spring AOP使用JDK動態代理或CGLIB為給定目標對象創建代理。 (只要有選擇,JDK動態代理就是首選)。
如果要代理的目標對象實現至少一個接口,則將使用JDK動態代理。 目標類型實現的所有接口都將被代理。 如果目標對象未實現任何接口,則將創建CGLIB代理。
如果要強制使用CGLIB代理(例如,代理為目標對象定義的每個方法,而不僅僅是那些由其接口實現的方法),您可以這樣做。 但是,有一些問題需要考慮:
無法建議最終方法,因為它們無法覆蓋。
您將在類路徑上需要CGLIB 2二進制文件,而JDK可以使用動態代理。 Spring會在需要CGLIB時自動發出警告,並且在類路徑中找不到CGLIB庫類。
代理對象的構造函數將被調用兩次。 這是CGLIB代理模型的自然結果,其中為每個代理對象生成子類。 對於每個代理實例,創建兩個對象:實際代理對象和實現建議的子類實例。 使用JDK代理時不會出現此行為。 通常,兩次調用代理類型的構造函數不是問題,因為通常只有賦值發生,並且構造函數中沒有實現真正的邏輯。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.