代理之JDK動(dòng)態(tài)代理原理(1)2018-07-27

回顧:

????上一篇代理之JDK動(dòng)態(tài)代理我們了解來JDK動(dòng)態(tài)代理的使用。同樣也遺留了兩個(gè)問題:1备埃、代理對(duì)象是怎么生成的闺魏?2、InvocationHandler的invoke方法是由誰來調(diào)用的晶框?

jdk動(dòng)態(tài)代理源碼:

????首先來看一下JDK是怎樣生成代理對(duì)象的。既然生成代理對(duì)象是用的Proxy類的靜態(tài)方newProxyInstance叁扫,那么我們就去它的源碼里看一下它到底都做了些什么三妈?

Java1.8 源碼(以下代碼代碼不全,只截取關(guān)鍵性代碼):

/**??

*?loader:類加載器??

*?interfaces:目標(biāo)對(duì)象實(shí)現(xiàn)的接口??

*?h:InvocationHandler的實(shí)現(xiàn)類??

*/??

public static Object newProxyInstance(ClassLoader loader,Class[] interfaces, ? ? ? ? ? ? ? ? ?

????????????????????????????????????InvocationHandler h)throws IllegalArgumentException{

? ? //判斷h不能為null莫绣。為null 拋出:NullPointerException

? ? ?Objects.requireNonNull(h);

? ? final Class[] intfs = interfaces.clone();

????????//查找或生成指定的代理類

? ? ? ?Class cl =getProxyClass0(loader, intfs);

? ? ? try {

????????//獲取構(gòu)造方法

????????final Constructor cons = cl.getConstructor();

? ? ? ? final InvocationHandler ih = h;

? ??????//如果構(gòu)造方法不是public 設(shè)置構(gòu)造方法為可訪問

? ? ? ? if (!Modifier.isPublic(cl.getModifiers())) {

????????????AccessController.doPrivileged(new PrivilegedAction() {

????????????????public Void run() {

????????????????????cons.setAccessible(true);

????????????????????return null;

? ? ? ? ? ? ? ? }

????????});????

? ? ? ? }

? ? ????/ /放射創(chuàng)建對(duì)象

????????return cons.newInstance(new Object[]{h});

? ? }catch (Exception e) {

????????????throw new InternalError(e.toString(), e);

? ? }

}

以上代碼可以看出:通過newProxyInstance方法是getProxyClass0獲取Class對(duì)象畴蒲,后通過反射創(chuàng)建對(duì)象。那getProxyClass0就看看getProxyClass0是什么獲取Class對(duì)象的:

private static ClassgetProxyClass0(ClassLoader loader, Class... interfaces) {

????//這里限制了目標(biāo)對(duì)象實(shí)現(xiàn)的接口的個(gè)數(shù)

?????if (interfaces.length >65535) {

????????????throw new IllegalArgumentException("interface limit exceeded");

? ? }

? ?return proxyClassCache.get(loader, interfaces);

}


/*

*如果緩存中有对室,就直接返回模燥,否則會(huì)生成

*key:類加載器咖祭;parameter:接口數(shù)組

*/

public V get(K key, P parameter) {

? ? //省略部分緩存機(jī)制代碼

? ? ...

? ? Factory factory =null;

? ??//下面用到了 CAS+重試 實(shí)現(xiàn)的多線程安全的 非阻塞算法

? ? while (true) {

? ? ? ? //最終執(zhí)行的代碼 從supplier.get()中獲取返回值

????????if (supplier !=null) {

? ? ? ? ? ? V value = supplier.get();

? ? ? ? ? ? if (value !=null) {

????????????????return value;

? ? ? ? ? ? }

????????}

? ? ? ? if (factory ==null) {

????????????factory =new Factory(key, parameter, subKey, valuesMap);

? ? ? ? }

????????if (supplier ==null) {

????????????supplier = valuesMap.putIfAbsent(subKey, factory);

? ? ? ? ? ? if (supplier ==null) {

? ? ? ? ? ? ? ? supplier = factory;

? ? ? ? ? ? }

? ? ? ? }else {

????????????if (valuesMap.replace(subKey, supplier, factory)) {

? ? ? ? ? ? ? ? supplier = factory;

? ? ? ? ? ? }else {

? ? ? ? ? ? ? ? supplier = valuesMap.get(subKey);

? ? ? ? ? ? }

????????}

????}

}

以上代碼重點(diǎn)是:?V value = supplier.get() 獲取最終的返回的class對(duì)象

public synchronized V get() {

? ? ? ? Supplier supplier =valuesMap.get(subKey);

? ? ? ? if (supplier !=this) {

? ? ? ? ? ? return null;

? ? ? ? }

? ? ? ? V value =null;

? ? ? ? try {

? ? ? ? ? ? //這里是創(chuàng)建返回Class對(duì)象(valueFactory.apply(key, parameter))

????????????value = Objects.requireNonNull(valueFactory.apply(key, parameter));

? ? ? ? }finally {

????????if (value ==null) {

? ? ? ? ? ? ? ? valuesMap.remove(subKey, this);

? ? ? ? ? ? }

}

? ? ? ? assert value !=null;

? ? ? ? CacheValue cacheValue =new CacheValue<>(value);

? ? ? ? reverseMap.put(cacheValue, Boolean.TRUE);

? ? ? ? if (!valuesMap.replace(subKey, this, cacheValue)) {

????????????throw new AssertionError("Should not reach here");

? ? ? ? }

?? ? ? ?return value;

? ? }

}

我們繼續(xù)關(guān)注:valueFactory.apply方法看看是什么創(chuàng)建Class對(duì)象的:

? ? public Classapply(ClassLoader loader, Class[] interfaces) {

????????Map, Boolean> interfaceSet =new IdentityHashMap<>(interfaces.length);

? ? ? ? for (Class intf : interfaces) {

? ? ? ? ? ? Class interfaceClass =null;

? ? ? ? ? ? try {

????????????????//?加載目標(biāo)類實(shí)現(xiàn)的接口到內(nèi)存中?

????????????????interfaceClass = Class.forName(intf.getName(), false, ????);

? ? ? ? ? ? }catch (ClassNotFoundException e) {

????????????}

????????????if (interfaceClass != intf) {

????????????????throw new IllegalArgumentException(intf +" is not visible from class loader");

? ? ? ? ? ? }

? ? ? ? ? ? //如果不是接口 就拋IllegalArgumentException

? ? ? ? ? ? if (!interfaceClass.isInterface()) {

????????????????throw new IllegalArgumentException(

????????????????????interfaceClass.getName() +" is not an interface");

? ? ? ? ? ? }

? ? ? ? ? ? if (interfaceSet.put(interfaceClass, Boolean.TRUE) !=null) {

????????????????throw new IllegalArgumentException(

????????????????????"repeated interface: " + interfaceClass.getName());

? ? ? ? ? ? }

}

????????String proxyPkg =null; ??

? ? ? ? int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

? ? ? ?// 驗(yàn)證所有非公共代理接口都在同一個(gè)包中。如果不是者拋異常

? ? ? ? for (Class intf : interfaces) {

????????????int flags = intf.getModifiers();

? ? ? ? ? ? if (!Modifier.isPublic(flags)) {

????????????accessFlags = Modifier.FINAL;

? ? ? ? ? ? ? ? String name = intf.getName();

? ? ? ? ? ? ? ? int n = name.lastIndexOf('.');

? ? ? ? ? ? ? ? String pkg = ((n == -1) ?"" : name.substring(0, n +1));

? ? ? ? ? ? ? ? if (proxyPkg ==null) {

????????????????????proxyPkg = pkg;

? ? ? ? ? ? ? ? }else if (!pkg.equals(proxyPkg)) {

????????????????????throw new IllegalArgumentException(

????????????????????????"non-public interfaces from different packages");

? ? ? ? ? ? ? ? }

????????????}

????????}

????????if (proxyPkg ==null) {

? ? ? ? ? ? proxyPkg = ReflectUtil.PROXY_PACKAGE +".";

? ? ? ? }

? ? ? ? long num =nextUniqueNumber.getAndIncrement();

? ? ? ? String proxyName = proxyPkg +proxyClassNamePrefix + num;

? ? ? ? //這個(gè)是重點(diǎn):生成類字節(jié)碼的方法

? ? ? ? byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

????????????????proxyName, interfaces, accessFlags);

? ? ? ? try {

? ??????????//?根據(jù)代理類的字節(jié)碼生成代理類的實(shí)例??

????????????return defineClass0(loader, proxyName, proxyClassFile, 0, ? ?

????????????????????????????????????????????????proxyClassFile.length);

? ? ? ? }catch (ClassFormatError e) {

? ? ? ? ? ? throw new IllegalArgumentException(e.toString());

? ? ? ? }

}

}

我們繼續(xù)跟蹤ProxyGenerator.generateProxyClass方法:????

public static byte[] generateProxyClass(final String var0, Class[] var1, int var2) {

????ProxyGenerator var3 =new ProxyGenerator(var0, var1, var2);

? ? //真正生成字節(jié)碼的方法

? ? final byte[] var4 = var3.generateClassFile();

//如果saveGeneratedFiles為true 則生成字節(jié)碼文件蔫骂,所以在開始我們要設(shè)置這個(gè)參數(shù)

? ? if (saveGeneratedFiles) {

????????????AccessController.doPrivileged(new PrivilegedAction() {

????????????????public Void run() {

????????????????try {

????????????????????int var1 = var0.lastIndexOf(46);

? ? ? ? ? ? ? ? ? ? Path var2;

? ? ? ? ? ? ? ? ? ? if (var1 >0) {

????????????Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));

? ? ? ? ? ? ? ? ? ? ? ? Files.createDirectories(var3);

? ? ? ? ? ? ? ? ? ? ? ? var2 = var3.resolve(var0.substring(var1 +1, var0.length()) +".class");

? ? ? ? ? ? ? ? ? ? }else {

????????????????????????var2 = Paths.get(var0 +".class");

? ? ? ? ? ? ? ? ? ? }

????????????????Files.write(var2, var4, new OpenOption[0]);

????????????????return null;

? ? ? ? ? ? ? ? }catch (IOException var4x) {

? ? ? ? ? ? ? ? ? ? ? ?throw new InternalError("I/O exception saving generated file: " + var4x);

? ? ? ? ? ? ? ? }

????????}

????????????});

? ? }

????????return var4;

}

var3.generateClassFile為正真創(chuàng)建字節(jié)碼的方法么翰,涉及字節(jié)碼文件的語法,在此我們就不在深入的探究辽旋。

總結(jié):代理對(duì)象是的創(chuàng)建過程一共分三個(gè)步驟:

? ??1浩嫌、ProxyGenerator.generateProxyClass方法負(fù)責(zé)生成代理類的字節(jié)碼,生成邏輯比較復(fù) ? ? ? ? ? ? ? ? ? ?雜补胚,了解原理繼續(xù)分析源碼?sun.misc.ProxyGenerator码耐;

????2、native方法Proxy.defineClass0負(fù)責(zé)字節(jié)碼加載的實(shí)現(xiàn)溶其,并返回對(duì)應(yīng)的Class對(duì)象骚腥。

? ??3、利用clazz.newInstance反射機(jī)制生成代理類的對(duì)象瓶逃;

至此我們明白了代理對(duì)象是怎么生成的束铭,那么InvocationHandler的invoke方法是由誰來調(diào)用的?厢绝?契沫?


“知其然,知其所以然”


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市代芜,隨后出現(xiàn)的幾起案子埠褪,更是在濱河造成了極大的恐慌,老刑警劉巖挤庇,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異贷掖,居然都是意外死亡嫡秕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門苹威,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昆咽,“玉大人,你說我怎么就攤上這事牙甫≈佬铮” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵窟哺,是天一觀的道長(zhǎng)泻轰。 經(jīng)常有香客問我,道長(zhǎng)且轨,這世上最難降的妖魔是什么浮声? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任虚婿,我火速辦了婚禮,結(jié)果婚禮上泳挥,老公的妹妹穿的比我還像新娘然痊。我一直安慰自己,他們只是感情好屉符,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布剧浸。 她就那樣靜靜地躺著,像睡著了一般矗钟。 火紅的嫁衣襯著肌膚如雪唆香。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天真仲,我揣著相機(jī)與錄音袋马,去河邊找鬼。 笑死秸应,一個(gè)胖子當(dāng)著我的面吹牛虑凛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播软啼,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼桑谍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了祸挪?” 一聲冷哼從身側(cè)響起锣披,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贿条,沒想到半個(gè)月后雹仿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡整以,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年胧辽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片公黑。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邑商,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凡蚜,到底是詐尸還是另有隱情人断,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布朝蜘,位于F島的核電站恶迈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芹务。R本人自食惡果不足惜蝉绷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一鸭廷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧熔吗,春花似錦辆床、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至中跌,卻和暖如春咨堤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漩符。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工一喘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗜暴。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓凸克,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親闷沥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子萎战,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容