摘要:原理動(dòng)態(tài)代理要求代理目標(biāo)必須是接口的實(shí)現(xiàn)類,通過接口生成模板類,模板類實(shí)現(xiàn)所有接口方法,實(shí)現(xiàn)方法是一個(gè)個(gè)模板方法,只是簡(jiǎn)單的通過反射把請(qǐng)求委托給處理。
使用 業(yè)務(wù)接口: IBiz
public interface IBiz { void doSomething(); }業(yè)務(wù)實(shí)現(xiàn)類: BizImpl
public class BizImpl implements IBiz { public void doSomething() { System.out.println("做一些業(yè)務(wù)邏輯"); } }橫切邏輯: PerformanceMonitor
public class PerformanceMonitor { public void start() { System.out.println("開始時(shí)間: " + String.valueOf(System.currentTimeMillis())); } public void end() { System.out.println("結(jié)束時(shí)間: " + String.valueOf(System.currentTimeMillis())); } }代理調(diào)用處理器: BizInvocationHandler
為接口生成的模板代理類,所有方法調(diào)用時(shí)都會(huì)委托給InvocationHandler.invoke(...)代為處理,它根據(jù)傳入的Method信息,使用反射機(jī)制調(diào)用真實(shí)的方法。
public class BizInvocationHandler implements InvocationHandler { private IBiz target; private PerformanceMonitor monitor; public BizInvocationHandler(IBiz target) { this.target = target; this.monitor = new PerformanceMonitor(); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { monitor.start(); method.invoke(target); monitor.end(); return null; } }代理生成器: ProxyBuilder
public class ProxyBuilder { private Class [] interfaces; private InvocationHandler handler; private ClassLoader classLoader = ProxyBuilder.class.getClassLoader(); public static ProxyBuilder newProxyBuilder() { return new ProxyBuilder(); } public ProxyBuilder setInterFaces(Class>[] interFaces) { this.interfaces = interFaces; return this; } public ProxyBuilder setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; return this; } public ProxyBuilder setInvocationHandler(InvocationHandler handler) { this.handler = handler; return this; } public Object build() { return Proxy.newProxyInstance(classLoader, interfaces, handler); } public void buildClassFile(String className, String dir) { byte[] proxyClassFile = ProxyGenerator.generateProxyClass(className, interfaces); StringBuilder strBuilder = new StringBuilder(); strBuilder.append(dir).append("/").append(className).append("class"); String classFileName = strBuilder.toString(); FileOutputStream out = null; try { out = new FileOutputStream(classFileName); out.write(proxyClassFile); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
其中build()生成代理對(duì)象;buildClassFile(...)生成代理類的class文件。
測(cè)試public class JDKProxyTest { @Test public void testBiz() { IBiz biz = new BizImpl(); BizInvocationHandler hander = new BizInvocationHandler(biz); IBiz proxy = (IBiz)ProxyBuilder.newProxyBuilder() .setClassLoader(Thread.currentThread().getContextClassLoader()) .setInterFaces(new Class[] {IBiz.class}) .setInvocationHandler(hander) .build(); proxy.doSomething(); } }
執(zhí)行輸出:
開始時(shí)間: 1500530510627 做一些業(yè)務(wù)邏輯 結(jié)束時(shí)間: 1500530510628使用小結(jié)
JDK動(dòng)態(tài)代理使用步驟如下:
實(shí)現(xiàn)InvocationHandler接口,為其載入代理的目標(biāo)實(shí)例&橫切邏輯,通過實(shí)現(xiàn)invoke方法實(shí)現(xiàn)橫切邏輯織入。
通過Proxy.newProxyInstance(...)把要代理的接口和InvocationHandler實(shí)例聯(lián)系起來生成最終的代理實(shí)例。
通過強(qiáng)制類型轉(zhuǎn)換可以把生成的代理實(shí)例轉(zhuǎn)換成任何一個(gè)代理的接口類型,從而調(diào)用接口方法。
原理JDK動(dòng)態(tài)代理要求代理目標(biāo)必須是接口的實(shí)現(xiàn)類,通過接口生成 模板類 ,模板類實(shí)現(xiàn)所有接口方法,實(shí)現(xiàn)方法是一個(gè)個(gè) 模板方法 ,只是簡(jiǎn)單的通過反射把請(qǐng)求委托給InvocationHandler.invoke(...)處理。
回頭看下ProxyBuilder.buildClassFile(...),它通過ProxyGenerator.generateProxyClass(...)生成IBiz的代理類。
生成代理類public class JDKProxyTest { @Test public void testBuildClassFile() { IBiz biz = new BizImpl(); BizInvocationHandler hander = new BizInvocationHandler(biz); ProxyBuilder.newProxyBuilder() .setClassLoader(Thread.currentThread().getContextClassLoader()) .setInterFaces(new Class[] {IBiz.class}) .setInvocationHandler(hander) .buildClassFile("proxy", "."); } }反編譯生成的代理類
直接通過Idea打開生成的proxy.class文件即可,反編譯后的代碼一下(注意:這里去掉了hashCode、toString等無強(qiáng)關(guān)聯(lián)性代碼):
public final class proxy extends Proxy implements IBiz { private static Method m1; ... public proxy(InvocationHandler var1) throws { super(var1); } public final void doSomething() throws { try { super.h.invoke(this, m1, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("jdkproxy.IBiz").getMethod("doSomething", new Class[0]); ... } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
哈哈,代碼非常簡(jiǎn)單,無謂多說。
代理類實(shí)例化過程入口: Proxy.newProxyInstance(...)
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { ... final Class>[] intfs = interfaces.clone(); // 獲取或生成指定接口的代理類,這里會(huì)對(duì)生成的代理類進(jìn)行緩存,下面展開。 Class> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { ... // 獲取代理類的構(gòu)造方法 final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { // 構(gòu)造方法不為public的話,修改其訪問屬性 AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } // 通過反射調(diào)用代理類的構(gòu)造方法實(shí)例化代理對(duì)象返回。 return cons.newInstance(new Object[]{h}); } catch ... ... }
Proxy.getProxyClass0(...)
private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces) { // 限制接口數(shù)量 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 這是一個(gè)WeakCach。 // 如果cache中存在由loader加載并且實(shí)現(xiàn)了interfaces接口的代理類,就直接返回。 // 否則就通過ProxyClassFactory創(chuàng)建代理類 // proxyClassCache = (new WeakCache<>(new KeyFactory(), new ProxyClassFactory());) return proxyClassCache.get(loader, interfaces); }
ProxyClassFactory.apply(...)
最終生成代理類的邏輯就在這里
public Class> apply(ClassLoader loader, Class>[] interfaces) { .... // 生成代理類的字節(jié)碼,buildClassFile.buildClassFile也是這樣生成代理類的。 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { // 返回定義的代理類 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { ... } }總結(jié)
通過JDK代理生成的代理類是一個(gè)模板類,它通過反射找到接口的所有方法,并為每一個(gè)方法生成模板方法,通過反射調(diào)InvocationHandler.invoke(...),通常業(yè)務(wù)邏輯和橫切都在這里被調(diào)用。
由于JDK代理生成的代理類相對(duì)cglib生成子類要輕量級(jí)一些,所以在生成代理的效率上要優(yōu)于cglib代理,但是在調(diào)用時(shí),GDK代理通過反射的方式調(diào)用,相對(duì)cglib直接調(diào)用效率上要低。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67434.html
摘要:是一種特殊的增強(qiáng)切面切面由切點(diǎn)和增強(qiáng)通知組成,它既包括了橫切邏輯的定義也包括了連接點(diǎn)的定義。實(shí)際上,一個(gè)的實(shí)現(xiàn)被拆分到多個(gè)類中在中聲明切面我們知道注解很方便,但是,要想使用注解的方式使用就必須要有源碼因?yàn)槲覀円? 前言 只有光頭才能變強(qiáng) 上一篇已經(jīng)講解了Spring IOC知識(shí)點(diǎn)一網(wǎng)打盡!,這篇主要是講解Spring的AOP模塊~ 之前我已經(jīng)寫過一篇關(guān)于AOP的文章了,那篇把比較重要的知...
摘要:總結(jié)動(dòng)態(tài)代理的相關(guān)原理已經(jīng)講解完畢,接下來讓我們回答以下幾個(gè)思考題。 【干貨點(diǎn)】 此處是【好好面試】系列文的第12篇文章。文章目標(biāo)主要是通過原理剖析的方式解答Aop動(dòng)態(tài)代理的面試熱點(diǎn)問題,通過一步步提出問題和了解原理的方式,我們可以記得更深更牢,進(jìn)而解決被面試官卡住喉嚨的情況。問題如下 SpringBoot默認(rèn)代理類型是什么 為什么不用靜態(tài)代理 JDK動(dòng)態(tài)代理原理 CGLIB動(dòng)態(tài)代理...
摘要:,,面向切面編程。,切點(diǎn),切面匹配連接點(diǎn)的點(diǎn),一般與切點(diǎn)表達(dá)式相關(guān),就是切面如何切點(diǎn)。例子中,注解就是切點(diǎn)表達(dá)式,匹配對(duì)應(yīng)的連接點(diǎn),通知,指在切面的某個(gè)特定的連接點(diǎn)上執(zhí)行的動(dòng)作。,織入,將作用在的過程。因?yàn)樵创a都是英文寫的。 之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動(dòng)態(tài)代理嗎?看這篇就夠了》介紹了下...
摘要:修飾者模式設(shè)計(jì)模式中的修飾者模式能動(dòng)態(tài)地給目標(biāo)對(duì)象增加額外的職責(zé)。修飾者模式調(diào)用的時(shí)序圖如下圖所示。的實(shí)現(xiàn)原理和修飾者模式類似。 ?在上邊一篇文章中我們介紹了Spring AOP的基本概念,今天我們就來學(xué)習(xí)一下與AOP實(shí)現(xiàn)相關(guān)的修飾者模式和Java Proxy相關(guān)的原理,為之后源碼分析打下基礎(chǔ)。 修飾者模式 ?Java設(shè)計(jì)模式中的修飾者模式能動(dòng)態(tài)地給目標(biāo)對(duì)象增加額外的職責(zé)(Respon...
摘要:本文首發(fā)于作者最近在學(xué),研究了下和代理模式,寫點(diǎn)心得和大家分享下。所以下面來重點(diǎn)分析下代理模式。這里代理模式分為靜態(tài)代理和動(dòng)態(tài)代理兩種,我們分別來看下。代理模式,代理,意味著有一方代替另一方完成一件事。 本文首發(fā)于 https://jaychen.cc作者 jaychen 最近在學(xué) Spring,研究了下 AOP 和代理模式,寫點(diǎn)心得和大家分享下。 AOP 先說下AOP,AOP 全稱 ...
閱讀 1046·2021-09-22 15:26
閱讀 2624·2021-09-09 11:52
閱讀 1917·2021-09-02 09:52
閱讀 2254·2021-08-12 13:28
閱讀 1192·2019-08-30 15:53
閱讀 522·2019-08-29 13:47
閱讀 3393·2019-08-29 11:00
閱讀 3105·2019-08-29 10:58