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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

cglib源码分析--转

發布時間:2025/4/5 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cglib源码分析--转 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文地址:http://www.iteye.com/topic/799827

背景

??? 前段時間在工作中,包括一些代碼閱讀過程中,spring aop經常性的會看到cglib中的相關內容,包括BeanCopier,BulkBean,Enancher等內容,以前雖大致知道一些內容,原理是通過bytecode,但沒具體深入代碼研究,只知其所用不知其所以然,所以就特地花了半天多的工作時間研究了CGLIB的相關源碼,同時結合看了下 spring Aop中對CGLIB的使用。

?? ?本文主要通過對cglib有原理的分析,反編譯查看源碼,例子等方式做一個介紹。

cglib基本信息

  • cglib的官方網站:?http://cglib.sourceforge.net/
  • cglib目前的最新版本應該是2.2,公司普遍使用的版本也是這個
  • 官網的samples :?http://cglib.sourceforge.net/xref/samples/
  • cglib代碼包結構

    • core (核心代碼)
      • EmitUtils
      • ReflectUtils
      • KeyFactory
      • ClassEmitter/CodeEmitter
      • NamingPolicy/DefaultNamingPolicy
      • GeneratorStrategy/DefaultGeneratorStrategy
      • DebuggingClassWriter
      • ClassGenerator/AbstractClassGenerator
    • beans (bean操作類)
      • BeanCopier
      • BulkBean
      • BeanMap
      • ImmutableBean
      • BeanGenerator
    • reflect
      • FastClass
    • proxy
      • Enhancer
      • CallbackGenerator
      • Callback
        • MethodInterceptor?, Dispatcher, LazyLoader , ProxyRefDispatcher , NoOp , FixedValue , InvocationHandler(提供和jdk proxy的功能)
      • CallbackFilter
    • util
      • StringSwitcher?
      • ParallelSorter?
    • transform?

    core核心代碼部分

    EmitUtils

    重要的工具類,主要封裝了一些操作bytecode的基本函數,比如生成一個null_constructor,添加類屬性add_property等

    ReflectUtils

    處理jdk reflect的工具類,比如獲取一個類所有的Method,獲取構造函數信息等。

    ClassEmitter/CodeEmitter

    對asm的classAdapter和MethodAdapter的實現,貫穿于cglib代碼的處理

    KeyFactory

    類庫中重要的唯一標識生成器,用于cglib做cache時做map key,比較底層的基礎類。
    例子:

    interface BulkBeanKey { public Object newInstance(String target, String[] getters, String[] setters, String[] types); } (BulkBeanKey)KeyFactory.create(BulkBeanKey.class).newInstance(targetClassName, getters, setters, typeClassNames);

    說明:

    • 每個Key接口,都必須提供newInstance方法,但具體的參數可以隨意定義,通過newInstance返回的為一個唯一標示,只有當傳入的所有參數的equals都返回true時,生成的key才是相同的,這就相當于多key的概念。

    NamingPolicy

    默認的實現類:DefaultNamingPolicy, 具體cglib動態生成類的命名控制。
    一般的命名規則:

    • 被代理class name + "$$" + 使用cglib處理的class name + "ByCGLIB" + "$$" + key的hashcode
    • 示例:FastSource$$FastClassByCGLIB$$e1a36bab.class

    GeneratorStrategy

    默認的實現類: DefaultGeneratorStrategy
    控制ClassGenerator生成class的byte數據,中間可插入自己的處理。注意這里依賴了:DebuggingClassWriter進行class generator的處理

    DebuggingClassWriter

    cglib封裝asm的處理類,用于生成class的byte流,通過GeneratorStrategy回調ClassGenerator.generateClass(DebuggingClassWriter),將自定義的class byte處理回調給具體的cglib上層操作類,比如由具體的BeanCopier去控制bytecode的生成。

    ClassGenerator

    其中一個抽象實現:AbstractClassGenerator。cglib代碼中核心的Class bytecode操作主體,包含了一些cache,調用NamingPolicy,GeneratorStrategy進行處理,可以說是一個最核心的調度者。

    ?

    ?

    對應的類圖:

    ?

  • 外部的BeanCopier都包含了一Generator,繼承自AbstractClassGenerator,實現了generateClass(ClassVisitor v),Object firstInstance(Class type)方法。
  • AbstractClassGenerator自身會根據Source進行cache,所以針對已經生成過的class,這里KeyFactory對應的值要相等,則會直接返回cache中的結果。所以BeanCopier每次create慢只是每次都需要new兩個對象,一個是KeyFactory.newInstance,另一個是firstInstance方法調用生成一個對象。
  • 反編譯tips

    大家都知道cglib是進行bytecode操作,會動態生成class,最快最直接的學習就是結合他生成的class,對照代碼進行學習,效果會好很多。

    Java代碼??
  • system.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"指定輸出目錄"); ??
  • ?可參見 cores/DebuggingClassWriter代碼。說明:這樣cglib會將動態生成的每個class都輸出到文件中,然后我們可以通過decomp進行反編譯查看源碼。

    ?

    beans (相關操作類)

    BeanCopier

    簡單的示例代碼就不做介紹,相信大家都指導怎么用,這里主要介紹下Convert的使用。

    • 許多網友都做過BeanCopier,BeanUtils的測試,基本BeanCopier的性能是BeanUtils的10倍以上。,出了反射這一性能差異外,BeanUtils默認是開啟Converter功能,允許同名,不同類型的屬性進行拷貝,比如Date對象到String屬性。
    • 有興趣的同學可以去比較下PropertyUtils,默認不開啟Converter功能,發現性能是BeanUtils的2倍多。

    初始化例子:BeanCopier copier = BeanCopier.create(Source.class, Target.class, true);?
    第三個參數useConverter,是否開啟Convert,默認BeanCopier只會做同名,同類型屬性的copier,否則就會報錯。

    Converter使用例子代碼??
  • public?class?BeanCopierTest?{??
  • ??
  • ????public?static?void?main(String?args[])?{??
  • ????????System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"/tmp/1");??
  • ????????BeanCopier?copier?=?BeanCopier.create(Source.class,?Target.class,?true);??
  • ????????Source?from?=?new?Source();??
  • ????????from.setValue(1);??
  • ??
  • ????????Target?to?=?new?Target();??
  • ????????Converter?converter?=?new?BigIntConverter();??
  • ????????copier.copy(from,?to,?converter);?//使用converter類??
  • ??
  • ????????System.out.println(to.getValue());??
  • ????}??
  • }??
  • ??
  • class?BigIntConverter?implements?net.sf.cglib.core.Converter?{??
  • ??
  • ????@Override??
  • ????public?Object?convert(Object?value,?Class?target,?Object?context)?{??
  • ????????System.out.println(value.getClass()?+?"?"?+?value);?//?from類中的value對象??
  • ????????System.out.println(target);?//?to類中的定義的參數對象??
  • ????????System.out.println(context.getClass()?+?"?"?+?context);?//?String對象,具體的方法名??
  • ????????if?(target.isAssignableFrom(BigInteger.class))?{??
  • ????????????return?new?BigInteger(value.toString());??
  • ????????}?else?{??
  • ????????????return?value;??
  • ????????}??
  • ????}??
  • ??
  • }??
  • ----??
  • 反編譯后看的代碼:??
  • public?class?Target$$BeanCopierByCGLIB$$e1c34377?extends?BeanCopier??
  • {??
  • ????public?void?copy(Object?obj,?Object?obj1,?Converter?converter)??
  • ????{??
  • ????????Target?target?=?(Target)obj1;??
  • ????????Source?source?=?(Source)obj;??
  • ????????//?注意是直接調用,沒有通過reflect??
  • ????????target.setValue((BigInteger)converter.convert(new?Integer(source.getValue()),?CGLIB$load_class$java$2Emath$2EBigInteger,?"setValue"));???
  • ????}??
  • }??
  • ?

    使用注意

  • 避免每次進行BeanCopier.create創建對象,一般建議是通過static BeanCopier copier = BeanCopier.create()
  • 合理使用converter。
  • 應用場景:兩個對象之間同名同屬性的數據拷貝,?不能單獨針對其中的幾個屬性單獨拷貝
  • BulkBean

    ???? 相比于BeanCopier,BulkBean將整個Copy的動作拆分為getPropertyValues,setPropertyValues的兩個方法,允許自定義處理的屬性。

    ?

    Java代碼??
  • public?class?BulkBeanTest?{??
  • ??
  • ????public?static?void?main(String?args[])?{??
  • ????????System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"/home/ljh/cglib");??
  • ????????String[]?getter?=?new?String[]?{?"getValue"?};??
  • ????????String[]?setter?=?new?String[]?{?"setValue"?};??
  • ????????Class[]?clazzs?=?new?Class[]?{?int.class?};??
  • ??
  • ????????BulkBean?bean?=?BulkBean.create(BulkSource.class,?getter,?setter,?clazzs);??
  • ????????BulkSource?obj?=?new?BulkSource();??
  • ????????obj.setValue(1);??
  • ??
  • ????????Object[]?objs?=?bean.getPropertyValues(obj);??
  • ????????for?(Object?tmp?:?objs)?{??
  • ????????????System.out.println(tmp);??
  • ????????}??
  • ????}??
  • }??
  • class?BulkSource?{??
  • ????private?int?value;??
  • ????.....??
  • }??
  • ??
  • //?反編譯后的代碼: ??
  • ?public?void?getPropertyValues(Object?obj,?Object?aobj[])??
  • ????{??
  • ????????BulkSource?bulksource?=?(BulkSource)obj;??
  • ????????aobj[0]?=?new?Integer(bulksource.getValue());??
  • ????}??
  • ?

    使用注意

  • 避免每次進行BulkBean.create創建對象,一般建議是通過static BulkBean.create copier = BulkBean.create
  • 應用場景:針對特定屬性的get,set操作,一般適用通過xml配置注入和注出的屬性,運行時才確定處理的Source,Target類,只需關注屬性名即可。
  • ?

    BeanMap

    相比于BeanCopier,BulkBean,都是針對兩個Pojo Bean進行處理,那如果對象一個是Pojo Bean和Map對象之間,那就得看看BeanMap,將一個java bean允許通過map的api進行調用。
    幾個支持的操作接口:

    • Object get(Object key)
    • Object put(Object key, Object value)
    • void putAll(Map t)
    • Set entrySet()
    • Collection values()
    • boolean containsKey(Object key)
    • ....
    Java代碼??
  • public?class?BeanMapTest?{??
  • ??
  • ????public?static?void?main(String?args[])?{??
  • ????????//?初始化??
  • ????????BeanMap?map?=?BeanMap.create(new?Pojo());??
  • ????????//?構造??
  • ????????Pojo?pojo?=?new?Pojo();??
  • ????????pojo.setIntValue(1);??
  • ????????pojo.setBigInteger(new?BigInteger("2"));??
  • ????????//?賦值??
  • ????????map.setBean(pojo);??
  • ????????//?驗證??
  • ????????System.out.println(map.get("intValue"));??
  • ????????System.out.println(map.keySet());??
  • ????????System.out.println(map.values());??
  • ????}??
  • }??
  • ??
  • class?Pojo?{??
  • ??
  • ????private?int????????intValue;??
  • ????private?BigInteger?bigInteger;??
  • ????....??
  • }??
  • ??
  • //反編譯代碼查看:??
  • //首先保存了所有的屬性到一個set中??
  • private?static?FixedKeySet?keys?=?new?FixedKeySet(new?String[]?{??
  • ????????"bigInteger",?"intValue"??
  • ????});??
  • public?Object?get(Object?obj,?Object?obj1)??
  • ????{??
  • ????????(Pojo)obj;??
  • ????????String?s?=?(String)obj1;??
  • ????????s;??
  • ????????s.hashCode();??
  • ????????JVM?INSTR?lookupswitch?2:?default?72??
  • ????//???????????????????-139068386:?40??
  • ????//???????????????????556050114:?52;??
  • ???????????goto?_L1?_L2?_L3??
  • _L2:??
  • ????????"bigInteger";??
  •  //屬性判斷是否相等??
  • ????????equals();??
  • ????????JVM?INSTR?ifeq?73;??
  • ???????????goto?_L4?_L5??
  • _L5:??
  • ????????break?MISSING_BLOCK_LABEL_73;??
  • _L4:??
  • ????????getBigInteger();??
  • ????????return;??
  • _L3:??
  • ??
  • ....??
  • ??
  • }??
  • ?

    ?

    使用注意

  • 避免每次進行BeanMap map = BeanMap.create();創建對象,不同于BeanCopier對象,BeanMap主要針對對象實例進行處理,所以一般建議是map.setBean(pojo);進行動態替換持有的對象實例。
  • 應用場景:針對put,putAll操作會直接修改pojo對象里的屬性,所以可以通過beanMap.putAll(map)進行map<->pojo屬性的拷貝。
  • ?

    BeanGenerator

    ?? 暫時沒有想到合適的使用場景,不過BeanGenerator使用概念是很簡單的,就是將一個Map<String,Class>properties的屬性定義,動態生成一個pojo bean類。

    ?

    Java代碼??
  • BeanGenerator?generator?=?new?BeanGenerator();??
  • generator.addProperty("intValue",?int.class);??
  • generator.addProperty("integer",?Integer.class);??
  • generator.addProperty("properties",?Properties.class);??
  • ?????????
  • Class?clazz?=?(Class)?generator.createClass();??
  • Object?obj?=?generator.create();??
  • ??
  • PropertyDescriptor[]?getters?=?ReflectUtils.getBeanGetters(obj.getClass());??
  • for?(PropertyDescriptor?getter?:?getters)?{??
  • ????Method?write?=?getter.getWriteMethod();??
  • ????System.out.println(write.getName());??
  • }??
  • ?

    ImmutableBean

    bean Immutable模式的一種動態class實現,Immutable模式主要應用于服務設計上,返回的pojo bean對象,不運行進行write方法調用。

    ?

    ?

    說明

    個人是不太建議使用cglib動態class的方式來實現bean Immutable的模式,Immutable模式應該是一種服務接口上的顯示聲明,而不是如此隱晦,而且pojo bean盡量做到是輕量級,簡答的set/get方法,如果要做充血的領域模型那就另當別論了。

    ?

    reflect (class,method處理)

    FastClass

    顧明思義,FastClass就是對Class對象進行特定的處理,比如通過數組保存method引用,因此FastClass引出了一個index下標的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的獲取method的方法。
    通過數組存儲method,constructor等class信息,從而將原先的反射調用,轉化為class.index的直接調用,從而體現所謂的FastClass。

    Java代碼??
  • public?class?FastClassTest?{??
  • ????public?static?void?main(String?args[])?throws?Exception?{??
  • ????????System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"/home/ljh/cglib");??
  • ??
  • ????????FastClass?clazz?=?FastClass.create(FastSource.class);??
  • ????????//?fast?class反射調用??
  • ????????FastSource?obj?=?(FastSource)?clazz.newInstance();??
  • ????????clazz.invoke("setValue",?new?Class[]?{?int.class?},?obj,?new?Object[]?{?1?});??
  • ????????clazz.invoke("setOther",?new?Class[]?{?int.class?},?obj,?new?Object[]?{?2?});??
  • ??
  • ????????int?value?=?(Integer)?clazz.invoke("getValue",?new?Class[]?{},?obj,?new?Object[]?{});??
  • ????????int?other?=?(Integer)?clazz.invoke("getOther",?new?Class[]?{},?obj,?new?Object[]?{});??
  • ????????System.out.println(value?+?"?"?+?other);??
  • ????????//?fastMethod使用??
  • ????????FastMethod?setValue?=?clazz.getMethod("setValue",?new?Class[]?{?int.class?});??
  • ????????System.out.println("setValue?index?is?:?"?+?setValue.getIndex());??
  • ??
  • ????????FastMethod?getValue?=?clazz.getMethod("getValue",?new?Class[]?{});??
  • ????????System.out.println("getValue?index?is?:?"?+?getValue.getIndex());??
  • ??
  • ????????FastMethod?setOther?=?clazz.getMethod("setOther",?new?Class[]?{?int.class?});??
  • ????????System.out.println("setOther?index?is?:?"?+?setOther.getIndex());??
  • ??
  • ????????FastMethod?getOther?=?clazz.getMethod("getOther",?new?Class[]?{});??
  • ????????System.out.println("getOther?index?is?:?"?+?getOther.getIndex());??
  • ????????//?其他??
  • ????????System.out.println("getDeclaredMethods?:?"?+?clazz.getJavaClass().getDeclaredMethods().length);??
  • ????????System.out.println("getConstructors?:?"?+?clazz.getJavaClass().getConstructors().length);??
  • ????????System.out.println("getFields?:?"?+?clazz.getJavaClass().getFields().length);??
  • ????????System.out.println("getMaxIndex?:?"?+?clazz.getMaxIndex());??
  • ????}??
  • }??
  • ??
  • class?FastSource?{??
  • ????private?int?value;??
  • ????private?int?other;??
  • ??
  • }??
  • ?

    proxy (spring aop相關)

    總體類結構圖:

    Callback & CallbackGenerator

  • MethodInterceptor
    • 類似于spring aop的around Advise的功能,大家都知道,不多做介紹。唯一需要注意的就是proxy.invokeSuper和proxy.invoke的區別。invokeSuper是退出當前interceptor的處理,進入下一個callback處理,invoke則會繼續回調該方法,如果傳遞給invoke的obj參數出錯容易造成遞歸調用
  • Dispatcher, ProxyRefDispatcher
    • 類似于delegate的模式,直接將請求分發給具體的Dispatcher調用,是否有著接口+實現分離的味道,將接口的方法調用通過Dispatcher轉到實現target上。ProxyRefDispatcher與Dispatcher想比,loadObject()多了個當前代理對象的引用。
    • 反編譯的部分代碼代碼??
    • //反編譯的部分代碼??
    • public?final?int?cal(int?i,?int?j)??
    • {??
    • ????????CGLIB$CALLBACK_1;??
    • ????????if(CGLIB$CALLBACK_1?!=?null)?goto?_L2;?else?goto?_L1??
    • _L1:??
    • ????????JVM?INSTR?pop?;??
    • ????????CGLIB$BIND_CALLBACKS(this);??
    • ????????CGLIB$CALLBACK_1;??
    • _L2:??
    • ????????loadObject();?//每次都進行調用??
    • ????????(DefaultCalcService);??
    • ????????i;??
    • ????????j;??
    • ????????cal();?//調用實現類的方法??
    • ????????return;??
    • ????}???
  • LazyLoader
    • 相比于Dispatcher,lazyLoader在第一次獲取了loadObject后,會進行緩存,后續的請求調用都會直接調用該緩存的屬性.
    • 反編譯部分代碼代碼??
    • //反編譯部分代碼??
    • public?final?int?cal(int?i,?int?j)??
    • {??
    • ????this;??
    • ????return?((DefaultCalcService)CGLIB$LOAD_PRIVATE_3()).cal(i,?j);??
    • }??
    • ??
    • private?final?synchronized?Object?CGLIB$LOAD_PRIVATE_3()??
    • {??
    • ????????CGLIB$LAZY_LOADER_3;?//保存的屬性??
    • ????????if(CGLIB$LAZY_LOADER_3?!=?null)?goto?_L2;?else?goto?_L1??
    • _L1:??
    • ????????JVM?INSTR?pop?;??
    • ????????this;??
    • ????????CGLIB$CALLBACK_3;??
    • ????????if(CGLIB$CALLBACK_3?!=?null)?goto?_L4;?else?goto?_L3??
    • _L3:??
    • ????????JVM?INSTR?pop?;??
    • ????????CGLIB$BIND_CALLBACKS(this);??
    • ????????CGLIB$CALLBACK_3;??
    • _L4:??
    • ????????loadObject();??
    • ????????JVM?INSTR?dup_x1?;??
    • ????????CGLIB$LAZY_LOADER_3;??
    • _L2:??
    • ????????return;??
    • ????}??
  • NoOp
    • 不做任何處理,結合Filter針對不需要做代理方法直接返回,調用其原始方法
  • FixedValue
    • 強制方法返回固定值,可結合Filter進行控制
  • InvocationHandler(提供和jdk proxy的功能),不常用
  • CallbackFilter

    主要的作用就是callback調度,主要的一個方法:int accept(Method method);?
    返回的int在int值,代表對應method需要插入的callback,會靜態生成到class的代碼中,這樣是cglib proxy區別于jdk proxy的方式,一個是靜態的代碼調用,一個是動態的reflect。
    可以查看: Enhancer類中的emitMethods方法,line:883。在構造class method字節嗎之前就已經確定需要運行的callback。

    ?

    Enhancer

    Java代碼??
  • System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"/home/ljh/cglib");??
  • LogInteceptor?logInteceptor?=?new?LogInteceptor();??
  • CalDispatcher?calDispatcher?=?new?CalDispatcher();??
  • CalcProxyRefDispatcher?calcProxyRefDispatcher?=?new?CalcProxyRefDispatcher();??
  • LazyLoaderCallback?lazyLoaderCallback?=?new?LazyLoaderCallback();??
  • ??
  • Enhancer?enhancer?=?new?Enhancer();??
  • enhancer.setSuperclass(CalcService.class);?//接口類??
  • enhancer.setCallbacks(new?Callback[]?{?logInteceptor,?calDispatcher,?calcProxyRefDispatcher,lazyLoaderCallback,?NoOp.INSTANCE?});?//?callback數組??
  • enhancer.setCallbackFilter(new?CalcCallbackFilter());?//?filter??
  • CalcService?service?=?(CalcService)?enhancer.create();??
  • ??
  • int?result?=?service.cal(1,?1);??
  • ?

    Util? (工具類,感覺有點雞肋)

    • StringSwitcher 提供string和int的map映射查詢,給定一個string字符串,返回同個下標數組的int值,感覺很雞肋,用Map不是可以很快速的實現功能
    • ParallelSorter 看了具體的代碼,沒啥意思,就是提供了一個二分的快速排序和多路歸并排序。沒有所謂的并行排序,原本以為會涉及多線程處理,可惜沒有

    ?

    transform

    ???? 暫時沒仔細研究,更多的是對asm的封裝,等下次看了asm代碼后再回來研究下。

    轉載于:https://www.cnblogs.com/davidwang456/p/5654097.html

    總結

    以上是生活随笔為你收集整理的cglib源码分析--转的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。