日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

cglib_cglib:缺少的手册

發(fā)布時間:2023/12/3 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cglib_cglib:缺少的手册 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

cglib

字節(jié)碼檢測庫cglib在許多眾所周知的Java框架(例如Hibernate (現(xiàn)在不再 )或Spring )中很受歡迎,它們可以完成骯臟的工作。 字節(jié)碼檢測允許在Java應(yīng)用程序的編譯階段之后操作或創(chuàng)建類。 由于Java類是在運行時動態(tài)鏈接的,因此可以將新類添加到已經(jīng)運行的Java程序中。 Hibernate例如使用cglib生成動態(tài)代理。 Hibernate不會返回您存儲在數(shù)據(jù)庫中的完整對象,而是會返回存儲類的檢測版本,該版本僅在需要時才從數(shù)據(jù)庫延遲加載某些值。 例如,在向方法調(diào)用添加安全約束時,Spring使用了cglib。 Spring安全性不會直接調(diào)用您的方法,而是會首先檢查指定的安全性檢查是否通過,并且僅在此驗證之后委托給您的實際方法。 cglib的另一種流行用法是在諸如mockito之類的模擬框架內(nèi),其中模擬只不過是插裝類 ,在插裝類中,方法被空的實現(xiàn)(加上一些跟蹤邏輯)所替代。

除了ASM (另一個基于cglib的非常高級的字節(jié)代碼操作庫)之外,cglib還提供了相當?shù)图壍淖止?jié)代碼轉(zhuǎn)換器,即使不了解已編譯的Java類的詳細信息,也可以使用它們。 不幸的是,cglib的文檔很短,并不是說基本上沒有。 除了2005年發(fā)表的一篇博客文章演示了Enhancer類外,沒有什么可找的。 這篇博客文章是試圖演示cglib及其不幸的是經(jīng)常尷尬的API。

增強劑

讓我們從Enhancer類(cglib庫中最常用的類)開始。 增強程序允許為非接口類型創(chuàng)建Java代理。 可以將Enhancer器與Java標準庫的Proxy類(在Java 1.3中引入)進行比較。 Enhancer動態(tài)創(chuàng)建給定類型的子類,但攔截所有方法調(diào)用。 除Proxy類外,它對類和接口類型均適用。 以下示例和下面的一些示例均基于此簡單的Java POJO:

public static class SampleClass {public String test(String input) {return "Hello world!";} }

使用cglib,可以使用Enhancer和FixedValue回調(diào)輕松地將test(String)方法的返回值替換為另一個值:

@Test public void testFixedValue() throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib!";}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null)); }

在上面的示例中,增強器將返回SampleClass的已檢測子類的實例,其中所有方法調(diào)用均返回固定值,該值是由上面的匿名FixedValue實現(xiàn)生成的。 該對象由Enhancer#create(Object...) ,其中該方法采用任意數(shù)量的參數(shù),這些參數(shù)用于選擇增強類的任何構(gòu)造函數(shù)。 (即使構(gòu)造函數(shù)只是Java字節(jié)碼級別上的方法, Enhancer類也不能構(gòu)造函數(shù)。它也不可以構(gòu)造static或final類。)如果只想創(chuàng)建一個類,而又不創(chuàng)建實例, Enhancer#createClass將創(chuàng)建一個Class實例,可用于動態(tài)創(chuàng)建實例。 增強類的所有構(gòu)造函數(shù)都可以在此動態(tài)生成的類中用作委托構(gòu)造函數(shù)。

請注意,在上面的示例中,將委派任何方法調(diào)用,還應(yīng)調(diào)用java.lang.Object定義的方法。 結(jié)果,對proxy.toString()的調(diào)用也將返回“ Hello cglib!”。 相比之下,對proxy.hashCode()的調(diào)用將導致ClassCastException因為即使Object#hashCode簽名需要原始整數(shù), FixedValue攔截器也始終返回String 。

可以得出的另一個結(jié)論是最終方法沒有被攔截。 這種方法的一個示例是Object#getClass ,在調(diào)用該方法時將返回類似“ SampleClass $$ EnhancerByCGLIB $$ e277c63c”的內(nèi)容。 此類名稱由cglib隨機生成,以避免命名沖突。 在程序代碼中使用顯式類型時,請注意增強型實例的不同類。 但是,由cglib生成的類將與增強類位于同一包中(因此可以覆蓋package-private方法)。 與最終方法類似,子類化方法導致無法增強最終類。 因此,像Hibernate這樣的框架無法持久化最終類。

接下來,讓我們看一個更強大的回調(diào)類InvocationHandler ,它也可以與Enhancer一起使用:

@Test public void testInvocationHandler() throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {return "Hello cglib!";} else {throw new RuntimeException("Do not know what to do.");}}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString()); }

此回調(diào)使我們可以就調(diào)用的方法進行回答。 但是,在InvocationHandler#invoke方法隨附的代理對象上調(diào)用方法時應(yīng)小心。 將使用相同的InvocationHandler調(diào)度對此方法的所有調(diào)用,因此可能導致無限循環(huán)。 為了避免這種情況,我們可以使用另一個回調(diào)分配器:

@Test public void testMethodInterceptor() throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {return "Hello cglib!";} else {proxy.invokeSuper(obj, args);}}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString());proxy.hashCode(); // Does not throw an exception or result in an endless loop. }

MethodInterceptor允許完全控制所攔截的方法,并提供一些實用程序來以其原始狀態(tài)調(diào)用增強類的方法。 但是為什么仍然要使用其他方法呢? 因為其他方法效率更高,并且cglib通常用于效率起著重要作用的邊緣案例框架。 MethodInterceptor的創(chuàng)建和鏈接需要例如生成不同類型的字節(jié)碼,以及創(chuàng)建InvocationHandler不需要的某些運行時對象。 因此,增強器還可以使用其他類:

  • LazyLoader :即使LazyLoader的唯一方法具有相同的方法簽名FixedValue的LazyLoader是在根本不同F(xiàn)ixedValue攔截。 LazyLoader實際上應(yīng)該返回增強類的子類的實例。 僅當在增強型對象上調(diào)用方法并將其存儲以供將來調(diào)用生成的代理時,才請求此實例。 如果您的對象創(chuàng)建昂貴而又不知道該對象是否會被使用,則這是有道理的。 請注意,必須同時為代理對象和延遲加載的對象調(diào)用增強類的某些構(gòu)造函數(shù)。 因此,請確保有另一個廉價的(可能是protected )構(gòu)造函數(shù)可用,或為代理使用接口類型。 您可以通過將參數(shù)提供給Enhancer#create(Object...)來選擇構(gòu)造的被調(diào)用方法。
  • Dispatcher : Dispatcher類似于LazyLoader但將在每次方法調(diào)用時調(diào)用,而不存儲已加載的對象。 這允許更改類的實現(xiàn)而無需更改對它的引用。 同樣,請注意必須同時為代理和生成的對象調(diào)用某些構(gòu)造函數(shù)。
  • ProxyRefDispatcher :此類包含對在其簽名中調(diào)用的代理對象的引用。 例如,這允許將方法調(diào)用委派給該代理的另一個方法。 請注意,如果從ProxyRefDispatcher#loadObject(Object)內(nèi)調(diào)用相同的方法,這很容易導致無限循環(huán),并且始終會導致無限循環(huán)。
  • NoOp : NoOp類的名稱不代表其名稱。 而是將每個方法調(diào)用委派給增強類的方法實現(xiàn)。

此時,最后兩個攔截器可能對您沒有意義。 當總是將方法調(diào)用始終委派給增強類時,為什么還要增強類呢? 你是對的。 這些攔截器僅應(yīng)與CallbackFilter一起使用,如以下代碼片段所示:

@Test public void testCallbackFilter() throws Exception {Enhancer enhancer = new Enhancer();CallbackHelper callbackHelper = new CallbackHelper(SampleClass.class, new Class[0]) {@Overrideprotected Object getCallback(Method method) {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {return new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib!";};}} else {return NoOp.INSTANCE; // A singleton provided by NoOp.}}};enhancer.setSuperclass(MyClass.class);enhancer.setCallbackFilter(callbackHelper);enhancer.setCallbacks(callbackHelper.getCallbacks());SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString());proxy.hashCode(); // Does not throw an exception or result in an endless loop. }

Enhancer實例在其Enhancer#setCallbackFilter(CallbackFilter)方法中接受CallbackFilter在此方法中,它希望將增強類的方法映射到Callback實例數(shù)組的數(shù)組索引。 在創(chuàng)建的代理上調(diào)用方法時, Enhancer將選擇相應(yīng)的攔截器,并將調(diào)用的方法分派到相應(yīng)的Callback (這是到目前為止引入的所有攔截器的標記接口)。 為了使該API不再那么笨拙,cglib提供了一個CallbackHelper ,它將代表一個CallbackFilter并可以為您創(chuàng)建一個Callback數(shù)組。 上面的增強對象在功能上將與MethodInterceptor示例中的對象等效,但是它使您可以編寫專用的攔截器,同時將對這些攔截器的調(diào)度邏輯分開。

它是如何工作的?

Enhancer創(chuàng)建類時,它將為創(chuàng)建后為增強類注冊為Callback每個攔截器設(shè)置一個創(chuàng)建private static字段。 這也意味著用cglib創(chuàng)建的類定義在創(chuàng)建后就不能重用,因為回調(diào)的注冊不會成為所生成類的初始化階段的一部分,而是由JVM初始化該類后由cglib手動準備的。 這也意味著用cglib創(chuàng)建的類在初始化后在技術(shù)上還沒有準備好,例如由于目標計算機中加載的類不存在回調(diào),因此無法通過電線發(fā)送。

取決于注冊攔截器,CGLIB可能記錄附加字段,諸如例如用于MethodInterceptor其中兩個private static字段(一個保持的反射Method和另一保持MethodProxy是在增強類或任何的截取)的每方法注冊它的子類。 請注意, MethodProxy過度使用了FastClass ,這會觸發(fā)其他類的創(chuàng)建,下面將對其進行詳細描述。

由于所有這些原因,使用Enhancer時要小心。 并且始終要防御性地注冊回調(diào)類型,因為例如MethodInterceptor將觸發(fā)創(chuàng)建其他類并在增強類中注冊其他static字段。 這特別危險,因為回調(diào)變量也作為static變量存儲在增強類中:這意味著回調(diào)實例永遠不會被垃圾回收(除非它們的ClassLoader是異常的)。 當使用匿名類對它們的外部類進行靜默引用時,這尤其危險。 回想一下上面的例子:

@Test public void testFixedValue() throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib!";}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null)); }

FixedValue的匿名子類幾乎無法從增強的SampleClass引用,因此匿名的FixedValue實例或包含@Test方法的類都不會被垃圾回收。 這會在您的應(yīng)用程序中引入討厭的內(nèi)存泄漏。 因此,請勿將非static內(nèi)部類與cglib一起使用。 (為了使示例簡短,我僅在此博客條目中使用它們。)

最后,永遠不要攔截Object#finalize() 。 由于cglib的子類化方法,截取finalize函數(shù)是通過覆蓋它來實現(xiàn)的,通常這是一個壞主意 。 攔截終結(jié)器的增強實例將被垃圾收集器以不同的方式對待,并且還將導致這些對象在JVM的終結(jié)器隊列中排隊。 另外,如果(偶然)在截獲的finalize調(diào)用中創(chuàng)建了對增強類的硬引用,則實際上已經(jīng)創(chuàng)建了一個不可收集的實例。 一般來說,這是您所不需要的。 請注意, final方法永遠不會被cglib攔截。 因此, Object#wait , Object#notify和Object#notifyAll不會帶來相同的問題。 但是請注意, Object#clone可能會被攔截,這是您可能不想執(zhí)行的操作。

不變的豆

cglib的ImmutableBean允許您創(chuàng)建一個不可變包裝器,類似于Collections#immutableSet 。 IllegalStateException (但是,不是Java API建議的UnsupportedOperationException可防止對基礎(chǔ)bean進行所有更改。 看著一些豆

public class SampleBean {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;} }

我們可以使這個bean不可變:

@Test(expected = IllegalStateException.class) public void testImmutableBean() throws Exception {SampleBean bean = new SampleBean();bean.setValue("Hello world!");SampleBean immutableBean = (SampleBean) ImmutableBean.create(bean);assertEquals("Hello world!", immutableBean.getValue());bean.setValue("Hello world, again!");assertEquals("Hello world, again!", immutableBean.getValue());immutableBean.setValue("Hello cglib!"); // Causes exception. }

從該示例可以明顯看出,不可變bean通過拋出IllegalStateException防止所有狀態(tài)更改。 但是,可以通過更改原始對象來更改Bean的狀態(tài)。 所有這些更改都將通過ImmutableBean反映出來。

豆產(chǎn)生器

BeanGenerator是cglib的另一個bean實用程序。 它將在運行時為您創(chuàng)建一個bean:

@Test public void testBeanGenerator() throws Exception {BeanGenerator beanGenerator = new BeanGenerator();beanGenerator.addProperty("value", String.class);Object myBean = beanGenerator.create();Method setter = myBean.getClass().getMethod("setValue", String.class);setter.invoke(myBean, "Hello cglib!");Method getter = myBean.getClass().getMethod("getValue");assertEquals("Hello cglib!", getter.invoke(myBean)); }

從該示例可以明顯BeanGenerator , BeanGenerator首先將一些屬性用作名稱/值對。 創(chuàng)建時, BeanGenerator創(chuàng)建訪問器

  • <type> get<name>()
  • void set<name>(<type>)

為了你。 當另一個庫需要通過反射解決的bean,但您在運行時不知道這些bean時,這可能很有用。 (一個示例是Apache Wicket ,它可以與bean一起使用。)

豆復印機

BeanCopier是另一個Bean實用程序,可通過其屬性值復制Bean。 考慮另一個與SampleBean具有相似屬性的bean:

public class OtherSampleBean {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;} }

現(xiàn)在,您可以將屬性從一個bean復制到另一個:

@Test public void testBeanCopier() throws Exception {BeanCopier copier = BeanCopier.create(SampleBean.class, OtherSampleBean.class, false);SampleBean bean = new SampleBean();myBean.setValue("Hello cglib!");OtherSampleBean otherBean = new OtherSampleBean();copier.copy(bean, otherBean, null);assertEquals("Hello cglib!", otherBean.getValue()); }

不受特定類型的限制。 BeanCopier#copy方法可以(最終)選擇Converter ,允許對每個bean屬性進行進一步的操作。 如果BeanCopier是使用false作為第三個構(gòu)造函數(shù)參數(shù)創(chuàng)建的,則Converter被忽略,因此可以為null 。

散裝豆

BulkBean允許通過數(shù)組而不是方法調(diào)用使用一組指定的bean訪問器:

@Test public void testBulkBean() throws Exception {BulkBean bulkBean = BulkBean.create(SampleBean.class,new String[]{"getValue"},new String[]{"setValue"},new Class[]{String.class});SampleBean bean = new SampleBean();bean.setValue("Hello world!");assertEquals(1, bulkBean.getPropertyValues(bean).length);assertEquals("Hello world!", bulkBean.getPropertyValues(bean)[0]);bulkBean.setPropertyValues(bean, new Object[] {"Hello cglib!"});assertEquals("Hello cglib!", bean.getValue()); }

BulkBean將getter名稱數(shù)組,setter名稱數(shù)組和屬性類型數(shù)組作為其構(gòu)造函數(shù)參數(shù)。 然后,可以通過BulkBean#getPropertyBalues(Object)將生成的檢測類提取為數(shù)組。 同樣,可以通過BulkBean#setPropertyBalues(Object, Object[])設(shè)置bean的屬性。

豆地圖

這是cglib庫中的最后一個bean實用程序。 BeanMap將bean的所有屬性轉(zhuǎn)換為String to- Object Java Map :

@Test public void testBeanGenerator() throws Exception {SampleBean bean = new SampleBean();BeanMap map = BeanMap.create(bean);bean.setValue("Hello cglib!");assertEquals("Hello cglib", map.get("value")); }

另外, BeanMap#newInstance(Object)方法允許通過重用相同的Class為其他bean創(chuàng)建映射。

重點工廠

KeyFactory工廠允許動態(tài)創(chuàng)建由多個值組成的鍵,這些鍵可在例如Map實現(xiàn)中使用。 為此, KeyFactory需要一些接口,該接口定義應(yīng)在此類鍵中使用的值。 此接口必須包含一個名為newInstance的方法,該方法返回Object 。 例如:

public interface SampleKeyFactory {Object newInstance(String first, int second); }

現(xiàn)在可以通過以下方式創(chuàng)建aa密鑰的實例:

@Test public void testKeyFactory() throws Exception {SampleKeyFactory keyFactory = (SampleKeyFactory) KeyFactory.create(Key.class);Object key = keyFactory.newInstance("foo", 42);Map<Object, String> map = new HashMap<Object, String>();map.put(key, "Hello cglib!");assertEquals("Hello cglib!", map.get(keyFactory.newInstance("foo", 42))); }

KeyFactory將確保Object#equals(Object)和Object#hashCode方法的正確實現(xiàn),以便可以在Map或Set使用生成的鍵對象。 在cglib庫中, KeyFactory在內(nèi)部也有很多使用。

混合蛋白

有些人可能已經(jīng)從其他編程語言(例如Ruby或Scala,其中mixin稱為trait)中了解了Mixin類的概念。 cglib Mixin允許將多個對象組合成一個對象。 但是,為此,這些對象必須由接口支持:

public interface Interface1 {String first(); }public interface Interface2 {String second(); }public class Class1 implements Interface1 {@Overridepublic String first() {return "first";} }public class Class2 implements Interface2 {@Overridepublic String second() {return "second";} }

現(xiàn)在,可以通過其他接口將Class1和Class2類合并為一個類:

public interface MixinInterface extends Interface1, Interface2 { /* empty */ }@Test public void testMixin() throws Exception {Mixin mixin = Mixin.create(new Class[]{Interface1.class, Interface2.class,MixinInterface.class}, new Object[]{new Class1(), new Class2()});MixinInterface mixinDelegate = (MixinInterface) mixin;assertEquals("first", mixinDelegate.first());assertEquals("second", mixinDelegate.second()); }

誠然, Mixin API相當尷尬,因為它需要用于Mixin的類來實現(xiàn)某些接口,這樣非儀表Java也可以解決該問題。

字符串切換器

StringSwitcher將String模擬為int Java Map :

@Test public void testStringSwitcher() throws Exception {String[] strings = new String[]{"one", "two"};int[] values = new int[]{10, 20};StringSwitcher stringSwitcher = StringSwitcher.create(strings, values, true);assertEquals(10, stringSwitcher.intValue("one"));assertEquals(20, stringSwitcher.intValue("two"));assertEquals(-1, stringSwitcher.intValue("three")); }

StringSwitcher允許在String上模擬switch命令,例如從Java 7開始就可以使用內(nèi)置的Java switch語句來實現(xiàn)。如果在Java 6或更低StringSwitcher中使用StringSwitcher確實為您的代碼增加了好處,但是仍然值得懷疑,我會個人不建議使用它。

接口制造商

InterfaceMaker會執(zhí)行其名稱所建議的操作:它動態(tài)創(chuàng)建一個新接口。

@Test public void testInterfaceMaker() throws Exception {Signature signature = new Signature("foo", Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE});InterfaceMaker interfaceMaker = new InterfaceMaker();interfaceMaker.add(signature, new Type[0]);Class iface = interfaceMaker.create();assertEquals(1, iface.getMethods().length);assertEquals("foo", iface.getMethods()[0].getName());assertEquals(double.class, iface.getMethods()[0].getReturnType()); }

除了cglib的任何其他公共API類之外,接口制造商還依賴于ASM類型。 在運行的應(yīng)用程序中創(chuàng)建接口幾乎沒有意義,因為接口僅表示一種類型,編譯器可以使用該類型來檢查類型。 但是,當您生成要在以后的開發(fā)中使用的代碼時,這可能很有意義。

方法委托

通過將方法調(diào)用綁定到某個接口, MethodDelegate可以模擬MethodDelegate C#的委托到特定方法。 例如,以下代碼會將SampleBean#getValue方法綁定到委托:

public interface BeanDelegate {String getValueFromDelegate(); }@Test public void testMethodDelegate() throws Exception {SampleBean bean = new SampleBean();bean.setValue("Hello cglib!");BeanDelegate delegate = (BeanDelegate) MethodDelegate.create(bean, "getValue", BeanDelegate.class);assertEquals("Hello world!", delegate.getValueFromDelegate()); }

但是,有一些注意事項:

  • 工廠方法MethodDelegate#create恰好將一個方法名稱作為第二個參數(shù)。 這是MethodDelegate將為您代理的方法。
  • 必須有一個沒有為對象定義參數(shù)的方法,該方法作為第一個參數(shù)提供給工廠方法。 因此, MethodDelegate強度不如可能強。
  • 第三個參數(shù)必須是僅包含一個參數(shù)的接口。 MethodDelegate實現(xiàn)此接口,并且可以MethodDelegate為該接口。 調(diào)用該方法時,它將在作為第一個參數(shù)的對象上調(diào)用代理方法。

此外,請考慮以下缺點:

  • cglib為每個代理創(chuàng)建一個新類。 最終,這會浪費您永久的一代堆空間
  • 您不能代理帶有參數(shù)的方法。
  • 如果您的接口帶有參數(shù),則在沒有引發(fā)異常的情況下方法委派將根本無法工作(返回值始終為null )。 如果您的接口需要其他返回類型(即使更通用),則將收到IllegalArgumentException 。

組播代表

MulticastDelegate工作方式與MethodDelegate略有不同,盡管它的目標是類似的功能。 為了使用MulticastDelegate ,我們需要一個實現(xiàn)接口的對象:

public interface DelegatationProvider {void setValue(String value); }public class SimpleMulticastBean implements DelegatationProvider {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;} }

基于此接口支持的bean,我們可以創(chuàng)建一個MulticastDelegate ,將對setValue(String)所有調(diào)用分派到實現(xiàn)DelegationProvider接口的幾個類:

@Test public void testMulticastDelegate() throws Exception {MulticastDelegate multicastDelegate = MulticastDelegate.create(DelegatationProvider.class);SimpleMulticastBean first = new SimpleMulticastBean();SimpleMulticastBean second = new SimpleMulticastBean();multicastDelegate = multicastDelegate.add(first);multicastDelegate = multicastDelegate.add(second);DelegatationProvider provider = (DelegatationProvider)multicastDelegate;provider.setValue("Hello world!");assertEquals("Hello world!", first.getValue());assertEquals("Hello world!", second.getValue()); }

同樣,還有一些缺點:

  • 這些對象需要實現(xiàn)單方法接口。 這對于第三方庫來說很糟糕,并且當您使用CGlib進行某些魔術(shù)操作 (該魔術(shù)暴露于常規(guī)代碼)時很尷尬。 另外,您可以輕松實現(xiàn)自己的委托(盡管沒有字節(jié)碼,但是我懷疑您在手動委托方面是否能贏得如此之多)。
  • 當您的代表返回值時,您將僅收到添加的最后一個代表的值。 所有其他返回值都將丟失(但在某些時候由多播委托檢索)。

建設(shè)者代表

ConstructorDelegate允許創(chuàng)建字節(jié)儀表工廠方法 。 為此,我們首先需要一個具有單一方法newInstance的接口,該方法返回一個Object并采用任意數(shù)量的參數(shù)以用于指定類的構(gòu)造函數(shù)調(diào)用。 例如,為了為SampleBean創(chuàng)建一個ConstructorDelegate ,我們需要以下代碼來調(diào)用SampleBean的默認(無參數(shù))構(gòu)造函數(shù):

public interface SampleBeanConstructorDelegate {Object newInstance(); }@Test public void testConstructorDelegate() throws Exception {SampleBeanConstructorDelegate constructorDelegate = (SampleBeanConstructorDelegate) ConstructorDelegate.create(SampleBean.class, SampleBeanConstructorDelegate.class);SampleBean bean = (SampleBean) constructorDelegate.newInstance();assertTrue(SampleBean.class.isAssignableFrom(bean.getClass())); }

平行分選機

當對數(shù)組數(shù)組進行排序時, ParallelSorter聲稱是Java標準庫的數(shù)組排序器的更快替代方法:

@Test public void testParallelSorter() throws Exception {Integer[][] value = {{4, 3, 9, 0},{2, 1, 6, 0}};ParallelSorter.create(value).mergeSort(0);for(Integer[] row : value) {int former = -1;for(int val : row) {assertTrue(former < val);former = val;}} }

ParallelSorter接受一個數(shù)組數(shù)組,并允許對數(shù)組的每一行應(yīng)用合并排序或快速排序。 使用時請務(wù)必小心:

  • 當使用基本數(shù)組時,您必須在示例中調(diào)用具有明確排序范圍的合并排序(例如, ParallelSorter.create(value).mergeSort(0, 0, 3) ,否則, ParallelSorter出現(xiàn)一個非常明顯的錯誤,即試圖將原始數(shù)組轉(zhuǎn)換為Object[]數(shù)組將導致ClassCastException 。
  • 如果數(shù)組行不均勻,則第一個參數(shù)將確定要考慮的行的長度。 不均勻的行將導致不考慮多余的值進行排序,或者導致ArrayIndexOutOfBoundException 。

就我個人而言,我懷疑ParallelSorter確實具有時間優(yōu)勢。 誠然,我還沒有嘗試對其進行基準測試。 如果您嘗試過,很高興在評論中聽到它。

快速班和快速成員

通過包裝Java類并提供與反射API類似的方法, FastClass承諾比Java反射API更快地調(diào)用方法:

@Test public void testFastClass() throws Exception {FastClass fastClass = FastClass.create(SampleBean.class);FastMethod fastMethod = fastClass.getMethod(SampleBean.class.getMethod("getValue"));MyBean myBean = new MyBean();myBean.setValue("Hello cglib!");assertTrue("Hello cglib!", fastMethod.invoke(myBean, new Object[0])); }

除了演示的FastMethod , FastClass還可以創(chuàng)建FastConstructor但不能創(chuàng)建快速字段。 但是FastClass如何比正常反射更快? Java反射由JNI執(zhí)行,其中方法調(diào)用由某些C代碼執(zhí)行。 FastClass創(chuàng)建一些字節(jié)代碼,這些代碼直接從JVM內(nèi)部調(diào)用該方法。 但是,HotSpot JVM的較新版本(可能還有許多其他現(xiàn)代JVM)都知道一個稱為膨脹的概念,在該概念中,當反射方法經(jīng)常執(zhí)行時,JVM會將反射方法調(diào)用轉(zhuǎn)換為FastClass 本機版本 。 您甚至可以通過將sun.reflect.inflationThreshold屬性設(shè)置為較低的值來控制此行為(至少在HotSpot JVM上)。 (默認值為15。)此屬性確定在執(zhí)行了幾次反射調(diào)用后,應(yīng)使用字節(jié)碼檢測版本替換JNI調(diào)用。 因此,我建議不要在現(xiàn)代JVM上使用FastClass ,但是它可以調(diào)整舊Java虛擬機上的性能。

cglib代理

cglib Proxy是本文開頭提到的Java Proxy類的重新實現(xiàn)。 旨在允許在Java 1.3之前的Java版本中使用Java庫的代理,并且僅在次要細節(jié)上有所不同。 但是,可以在Java標準庫的Proxy javadoc中找到cglib Proxy的更好文檔,其中提供了其用法示例。 因此,我將在這里跳過對cglib Proxy的更詳細的討論。

最后的警告

在對cglib功能進行了概述之后,我想說最后一個警告。 所有cglib類都會生成字節(jié)碼,這會導致其他類存儲在JVM內(nèi)存的特殊部分中:所謂的燙發(fā)空間。 顧名思義,該永久空間用于通常不收集垃圾的永久對象。 但是,這不是完全正確的:加載Class ,在加載的ClassLoader可用于垃圾回收之前,無法將其卸載。 僅在用自定義ClassLoader加載Class的情況下,該ClassLoader不是本機JVM系統(tǒng)ClassLoader 。 這個ClassLoader可以,如果本身,都被垃圾收集Class ES IT不斷加載,并且所有的所有實例Class ES IT負載曾經(jīng)成為可進行垃圾回收。 這意味著:如果您在Java應(yīng)用程序的整個生命周期中創(chuàng)建了越來越多的類,并且如果您不照顧這些類的刪除,那么您將早晚運行燙發(fā)空間,這將導致應(yīng)用程序的死亡。 OutOfMemoryError手 。 因此,請謹慎使用cglib。 但是,如果您明智而謹慎地使用cglib,則可以用它做真正令人驚奇的事情,這超出了非儀器化Java應(yīng)用程序可以做的事情。

最后,在創(chuàng)建依賴于cglib的項目時,考慮到它的流行性,您應(yīng)該意識到cglib項目沒有得到應(yīng)有的維護和活動。 缺少的文檔是第一個提示。 通常是一團糟的公共API。 但隨后,cglib到Maven Central的部署也出現(xiàn)了中斷。 郵件列表的閱讀方式就像垃圾郵件的存檔一樣。 并且釋放周期相當不穩(wěn)定。 因此,您可能想看看javassist ,它是cglib的唯一真正的低級替代品。 Javassist捆綁了一個偽Java編譯器,該編譯器無需了解Java字節(jié)碼就可以創(chuàng)建非常驚人的字節(jié)碼工具。 如果您想弄臟手,您可能還喜歡在cglib之上構(gòu)建的ASM 。 ASM附帶了關(guān)于庫和Java類文件及其字節(jié)碼的出色文檔。

參考: cglib:我們的JCG合作伙伴 Rafael Winterhalter在My daily Java blog上缺少的手冊 。

翻譯自: https://www.javacodegeeks.com/2013/12/cglib-the-missing-manual.html

cglib

總結(jié)

以上是生活随笔為你收集整理的cglib_cglib:缺少的手册的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。