cglib源码学习交流
背景
??? 前段時間在工作中,包括一些代碼閱讀過程中,spring aop經(jīng)常性的會看到cglib中的相關內(nèi)容,包括BeanCopier,BulkBean,Enancher等內(nèi)容,以前雖大致知道一些內(nèi)容,原理是通過bytecode,但沒具體深入代碼研究,只知其所用不知其所以然,所以就特地花了半天多的工作時間研究了CGLIB的相關源碼,同時結合看了下 spring Aop中對CGLIB的使用。
cglib基本信息
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的基本函數(shù),比如生成一個null_constructor,添加類屬性add_property等
ReflectUtils
處理jdk reflect的工具類,比如獲取一個類所有的Method,獲取構造函數(shù)信息等。
ClassEmitter/CodeEmitter
對asm的classAdapter和MethodAdapter的實現(xiàn),貫穿于cglib代碼的處理
KeyFactory
類庫中重要的唯一標識生成器,用于cglib做cache時做map key,比較底層的基礎類。
例子:
public?Object?newInstance(String?target,?String[]?getters,?String[]?setters,?String[]?types);
}
(BulkBeanKey)KeyFactory.create(BulkBeanKey.class).newInstance(targetClassName,?getters,?setters,?typeClassNames);
說明:
- 每個Key接口,都必須提供newInstance方法,但具體的參數(shù)可以隨意定義,通過newInstance返回的為一個唯一標示,只有當傳入的所有參數(shù)的equals都返回true時,生成的key才是相同的,這就相當于多key的概念。
NamingPolicy
默認的實現(xiàn)類:DefaultNamingPolicy, 具體cglib動態(tài)生成類的命名控制。
一般的命名規(guī)則:
- 被代理class name + "$$" + 使用cglib處理的class name + "ByCGLIB" + "$$" + key的hashcode
- 示例:FastSource$$FastClassByCGLIB$$e1a36bab.class
GeneratorStrategy
默認的實現(xiàn)類: DefaultGeneratorStrategy
控制ClassGenerator生成class的byte數(shù)據(jù),中間可插入自己的處理。注意這里依賴了:DebuggingClassWriter進行class generator的處理
DebuggingClassWriter
cglib封裝asm的處理類,用于生成class的byte流,通過GeneratorStrategy回調ClassGenerator.generateClass(DebuggingClassWriter),將自定義的class byte處理回調給具體的cglib上層操作類,比如由具體的BeanCopier去控制bytecode的生成。
ClassGenerator
其中一個抽象實現(xiàn):AbstractClassGenerator。cglib代碼中核心的Class bytecode操作主體,包含了一些cache,調用NamingPolicy,GeneratorStrategy進行處理,可以說是一個最核心的調度者。
?
?
對應的類圖:
?
反編譯tips
大家都知道cglib是進行bytecode操作,會動態(tài)生成class,最快最直接的學習就是結合他生成的class,對照代碼進行學習,效果會好很多。
system.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"指定輸出目錄");?可參見 cores/DebuggingClassWriter代碼。說明:這樣cglib會將動態(tài)生成的每個class都輸出到文件中,然后我們可以通過decomp進行反編譯查看源碼。
?
beans (相關操作類)
BeanCopier
簡單的示例代碼就不做介紹,相信大家都指導怎么用,這里主要介紹下Convert的使用。
- 許多網(wǎng)友都做過BeanCopier,BeanUtils的測試,基本BeanCopier的性能是BeanUtils的10倍以上。,出了反射這一性能差異外,BeanUtils默認是開啟Converter功能,允許同名,不同類型的屬性進行拷貝,比如Date對象到String屬性。
- 有興趣的同學可以去比較下PropertyUtils,默認不開啟Converter功能,發(fā)現(xiàn)性能是BeanUtils的2倍多。
初始化例子:BeanCopier copier = BeanCopier.create(Source.class, Target.class, true);?
第三個參數(shù)useConverter,是否開啟Convert,默認BeanCopier只會做同名,同類型屬性的copier,否則就會報錯。
????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類中的定義的參數(shù)對象
????????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"));?
????}
}
使用注意
BulkBean
???? 相比于BeanCopier,BulkBean將整個Copy的動作拆分為getPropertyValues,setPropertyValues的兩個方法,允許自定義處理的屬性。
?
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());
????}
?
?
?
使用注意
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)
- ....
????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:
.
}
?
使用注意
?
BeanGenerator
?? 暫時沒有想到合適的使用場景,不過BeanGenerator使用概念是很簡單的,就是將一個Map<String,Class>properties的屬性定義,動態(tài)生成一個pojo bean類。
?
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模式的一種動態(tài)class實現(xiàn),Immutable模式主要應用于服務設計上,返回的pojo bean對象,不運行進行write方法調用
| 說明 個人是不太建議使用cglib動態(tài)class的方式來實現(xiàn)bean Immutable的模式,Immutable模式應該是一種服務接口上的顯示聲明,而不是如此隱晦,而且pojo bean盡量做到是輕量級,簡答的set/get方法,如果要做充血的領域模型那就另當別論了。 |
?
reflect (class,method處理)
FastClass
顧明思義,FastClass就是對Class對象進行特定的處理,比如通過數(shù)組保存method引用,因此FastClass引出了一個index下標的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的獲取method的方法。
通過數(shù)組存儲method,constructor等class信息,從而將原先的反射調用,轉化為class.index的直接調用,從而體現(xiàn)所謂的FastClass。
????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
- 類似于spring aop的around Advise的功能,大家都知道,不多做介紹。唯一需要注意的就是proxy.invokeSuper和proxy.invoke的區(qū)別。invokeSuper是退出當前interceptor的處理,進入下一個callback處理,invoke則會繼續(xù)回調該方法,如果傳遞給invoke的obj參數(shù)出錯容易造成遞歸調用
- 類似于delegate的模式,直接將請求分發(fā)給具體的Dispatcher調用,是否有著接口+實現(xiàn)分離的味道,將接口的方法調用通過Dispatcher轉到實現(xiàn)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();?//調用實現(xiàn)類的方法
????????return;
????}? ?
- 相比于Dispatcher,lazyLoader在第一次獲取了loadObject后,會進行緩存,后續(xù)的請求調用都會直接調用該緩存的屬性.
- //反編譯部分代碼
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;
????}
- 不做任何處理,結合Filter針對不需要做代理方法直接返回,調用其原始方法
- 強制方法返回固定值,可結合Filter進行控制
CallbackFilter
主要的作用就是callback調度,主要的一個方法:int accept(Method method);?
返回的int在int值,代表對應method需要插入的callback,會靜態(tài)生成到class的代碼中,這樣是cglib proxy區(qū)別于jdk proxy的方式,一個是靜態(tài)的代碼調用,一個是動態(tài)的reflect。
可以查看: Enhancer類中的emitMethods方法,line:883。在構造class method字節(jié)嗎之前就已經(jīng)確定需要運行的callback。
?
Enhancer
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數(shù)組
enhancer.setCallbackFilter(new?CalcCallbackFilter());?//?filter
CalcService?service?=?(CalcService)?enhancer.create();
int?result?=?service.cal(1,?1);
Util? (工具類,感覺有點雞肋)
- StringSwitcher 提供string和int的map映射查詢,給定一個string字符串,返回同個下標數(shù)組的int值,感覺很雞肋,用Map不是可以很快速的實現(xiàn)功能
- ParallelSorter 看了具體的代碼,沒啥意思,就是提供了一個二分的快速排序和多路歸并排序。沒有所謂的并行排序,原本以為會涉及多線程處理,可惜沒有
transform
???? 暫時沒仔細研究,更多的是對asm的封裝,等下次看了asm代碼后再回來研究下。
http://www.blogjava.net/agapple/archive/2010/11/01/336730.html
?
轉載于:https://www.cnblogs.com/softidea/p/5608962.html
總結
以上是生活随笔為你收集整理的cglib源码学习交流的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Http 四种请求访问代码 HttpGe
- 下一篇: 大数据Java基础第十二天作业