摘要:原理動態代理要求代理目標必須是接口的實現類,通過接口生成模板類,模板類實現所有接口方法,實現方法是一個個模板方法,只是簡單的通過反射把請求委托給處理。
使用 業務接口: IBiz
public interface IBiz { void doSomething(); }業務實現類: BizImpl
public class BizImpl implements IBiz { public void doSomething() { System.out.println("做一些業務邏輯"); } }橫切邏輯: PerformanceMonitor
public class PerformanceMonitor { public void start() { System.out.println("開始時間: " + String.valueOf(System.currentTimeMillis())); } public void end() { System.out.println("結束時間: " + String.valueOf(System.currentTimeMillis())); } }代理調用處理器: BizInvocationHandler
為接口生成的模板代理類,所有方法調用時都會委托給InvocationHandler.invoke(...)代為處理,它根據傳入的Method信息,使用反射機制調用真實的方法。
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()生成代理對象;buildClassFile(...)生成代理類的class文件。
測試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(); } }
執行輸出:
開始時間: 1500530510627 做一些業務邏輯 結束時間: 1500530510628使用小結
JDK動態代理使用步驟如下:
實現InvocationHandler接口,為其載入代理的目標實例&橫切邏輯,通過實現invoke方法實現橫切邏輯織入。
通過Proxy.newProxyInstance(...)把要代理的接口和InvocationHandler實例聯系起來生成最終的代理實例。
通過強制類型轉換可以把生成的代理實例轉換成任何一個代理的接口類型,從而調用接口方法。
原理JDK動態代理要求代理目標必須是接口的實現類,通過接口生成 模板類 ,模板類實現所有接口方法,實現方法是一個個 模板方法 ,只是簡單的通過反射把請求委托給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等無強關聯性代碼):
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()); } } }
哈哈,代碼非常簡單,無謂多說。
代理類實例化過程入口: Proxy.newProxyInstance(...)
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { ... final Class>[] intfs = interfaces.clone(); // 獲取或生成指定接口的代理類,這里會對生成的代理類進行緩存,下面展開。 Class> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { ... // 獲取代理類的構造方法 final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { // 構造方法不為public的話,修改其訪問屬性 AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } // 通過反射調用代理類的構造方法實例化代理對象返回。 return cons.newInstance(new Object[]{h}); } catch ... ... }
Proxy.getProxyClass0(...)
private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces) { // 限制接口數量 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 這是一個WeakCach。 // 如果cache中存在由loader加載并且實現了interfaces接口的代理類,就直接返回。 // 否則就通過ProxyClassFactory創建代理類 // proxyClassCache = (new WeakCache<>(new KeyFactory(), new ProxyClassFactory());) return proxyClassCache.get(loader, interfaces); }
ProxyClassFactory.apply(...)
最終生成代理類的邏輯就在這里
public Class> apply(ClassLoader loader, Class>[] interfaces) { .... // 生成代理類的字節碼,buildClassFile.buildClassFile也是這樣生成代理類的。 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { // 返回定義的代理類 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { ... } }總結
通過JDK代理生成的代理類是一個模板類,它通過反射找到接口的所有方法,并為每一個方法生成模板方法,通過反射調InvocationHandler.invoke(...),通常業務邏輯和橫切都在這里被調用。
由于JDK代理生成的代理類相對cglib生成子類要輕量級一些,所以在生成代理的效率上要優于cglib代理,但是在調用時,GDK代理通過反射的方式調用,相對cglib直接調用效率上要低。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67434.html
摘要:是一種特殊的增強切面切面由切點和增強通知組成,它既包括了橫切邏輯的定義也包括了連接點的定義。實際上,一個的實現被拆分到多個類中在中聲明切面我們知道注解很方便,但是,要想使用注解的方式使用就必須要有源碼因為我們要 前言 只有光頭才能變強 上一篇已經講解了Spring IOC知識點一網打盡!,這篇主要是講解Spring的AOP模塊~ 之前我已經寫過一篇關于AOP的文章了,那篇把比較重要的知...
摘要:總結動態代理的相關原理已經講解完畢,接下來讓我們回答以下幾個思考題。 【干貨點】 此處是【好好面試】系列文的第12篇文章。文章目標主要是通過原理剖析的方式解答Aop動態代理的面試熱點問題,通過一步步提出問題和了解原理的方式,我們可以記得更深更牢,進而解決被面試官卡住喉嚨的情況。問題如下 SpringBoot默認代理類型是什么 為什么不用靜態代理 JDK動態代理原理 CGLIB動態代理...
摘要:,,面向切面編程。,切點,切面匹配連接點的點,一般與切點表達式相關,就是切面如何切點。例子中,注解就是切點表達式,匹配對應的連接點,通知,指在切面的某個特定的連接點上執行的動作。,織入,將作用在的過程。因為源碼都是英文寫的。 之前《零基礎帶你看Spring源碼——IOC控制反轉》詳細講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態代理嗎?看這篇就夠了》介紹了下...
摘要:修飾者模式設計模式中的修飾者模式能動態地給目標對象增加額外的職責。修飾者模式調用的時序圖如下圖所示。的實現原理和修飾者模式類似。 ?在上邊一篇文章中我們介紹了Spring AOP的基本概念,今天我們就來學習一下與AOP實現相關的修飾者模式和Java Proxy相關的原理,為之后源碼分析打下基礎。 修飾者模式 ?Java設計模式中的修飾者模式能動態地給目標對象增加額外的職責(Respon...
摘要:本文首發于作者最近在學,研究了下和代理模式,寫點心得和大家分享下。所以下面來重點分析下代理模式。這里代理模式分為靜態代理和動態代理兩種,我們分別來看下。代理模式,代理,意味著有一方代替另一方完成一件事。 本文首發于 https://jaychen.cc作者 jaychen 最近在學 Spring,研究了下 AOP 和代理模式,寫點心得和大家分享下。 AOP 先說下AOP,AOP 全稱 ...
閱讀 1036·2021-09-22 15:26
閱讀 2615·2021-09-09 11:52
閱讀 1901·2021-09-02 09:52
閱讀 2248·2021-08-12 13:28
閱讀 1187·2019-08-30 15:53
閱讀 514·2019-08-29 13:47
閱讀 3388·2019-08-29 11:00
閱讀 3101·2019-08-29 10:58