JDK動態(tài)代理詳細解析
一、說明
在Java的動態(tài)代理機制中,有兩個重要的類和接口,一個是InvoInvocationHandler(接口)、Proxy(類),這一個類和接口是我們動態(tài)代理所必須用到的。
優(yōu)點:
- 對于實現(xiàn)了接口的類,可以直接使用基于接口的動態(tài)代理進行代理,非常方便
- 代理類和被代理類都必須實現(xiàn)同一個接口,能夠?qū)崿F(xiàn)對被代理對象的方法調(diào)用進行統(tǒng)一管理。
- 性能上:在老版的jdk,jdk代理生成的類速度快,通過反射調(diào)用慢,cglib是jdk代理速度的10倍左右,jdk在版本每次升級都會有很大的性能提升,cglib停滯不前,jdk7 8的動態(tài)代理性能在1萬次實驗中比cglib要快20%左右
- jdk動態(tài)代理如果目標類未實現(xiàn)接口則無法代理,cglib是通過繼承的方式來動態(tài)代理,若目標類被final關(guān)鍵字修飾,則無法使用cglib做動態(tài)代理
缺點:
- 只能代理實現(xiàn)了接口的類,對于沒有實現(xiàn)接口的類無法使用此種方式進行代理。
- jdk動態(tài)代理只提供實現(xiàn)接口的目標類代理,不支持沒有實現(xiàn)接口的目標類的代理。如果目標類沒有實現(xiàn)接口,只能用cglib代理
二、主要類方法的說明
InvocationHandler接口
每個動態(tài)代理類都必須實現(xiàn)InvocationHandler接口,并且每個代理類的實例都關(guān)聯(lián)到了一個handler,當我們通過代理對象調(diào)用一個方法的時候,這個方法的調(diào)用就會被轉(zhuǎn)發(fā)為由InvocationHandler接口的invoke方法來進行調(diào)用。
InvocationHandler接口的invoke方法
- Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- proxy: - 指代我們所代理的那個真實對象
- method: - 指代的是我們所要調(diào)用真實對象的某個方法的Method對象
- args: - 指代的是調(diào)用真實對象某個方法時接受的參數(shù)
**proxy存在的意義:**
1. 可以使用反射獲取代理對象的信息(也就是proxy.getClass().getName())。
2. 可以將代理對象返回以進行連續(xù)調(diào)用,這就是proxy存在的目的,因為this并不是代理對象。
Proxy 類
Proxy類的作用就是用來動態(tài)創(chuàng)建一個代理類對象的類,它提供了許多的方法,但是我們用的最多的就是newProxyInstance這個方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
- loader: 一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載
- interfaces: 一個Interface對象的數(shù)組,表示的是我將要給我需要代理的對象提供一組什么接口,如果我提供了一組接口給它,那么這個代理對象就宣稱實現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
- h: 一個InvocationHandler對象,表示的是當我這個動態(tài)代理對象在調(diào)用方法的時候,會關(guān)聯(lián)到哪一個InvocationHandler對象上
打印代理的類:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
三、關(guān)鍵步驟
創(chuàng)建InvocationHandler實現(xiàn)類
public class MapperProxy<T> implements InvocationHandler { private Class<T> proxyInterface; //這里可以維護一個緩存,存這個接口的方法抽象的對象 MapperProxy(Class<T> proxyInterface){ this.proxyInterface = proxyInterface; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("執(zhí)行前..."); Constructor constructor = proxyInterface.getConstructor(); Object o = constructor.newInstance(); method.invoke(o,args); System.out.println("執(zhí)行后"); return null; } }
通過 Proxy.newProxyInstance() 創(chuàng)建代理實例
Fly fly = (Fly) Proxy.newProxyInstance(MyFly.class.getClassLoader(),new Class[]{Fly.class, Fly.Fly2.class},new MapperProxy<>(MyFly.class));
- 創(chuàng)建代理類- Class<?> cl = getProxyClass0(loader, intfs);
- 獲取有參構(gòu)造器 Constructor<?> cons = cl.getConstructor(constructorParams);這里的參數(shù)就是InvocationHandler
- 通過構(gòu)造器創(chuàng)建代理實例-參數(shù)就是方法的第三個參數(shù)
分析生成的代理類
結(jié)論
- 代理類會繼承Proxy ,這里也就解釋了為什么通過JDK生成的代理無法代理非接口實現(xiàn)類了
- 代理類實現(xiàn)了傳入的所有接口類型
調(diào)用代理類的doFly()
這里會調(diào)用 super.h.invoke(this, m3, (Object[])null);
super.h就是我們再創(chuàng)建代理對象是傳入的MapperProxy,所有這里會執(zhí)行MapperProxy.invoke方法(在這里我們就可動態(tài)的對該執(zhí)行方法進行增強)
到此這篇關(guān)于JDK動態(tài)代理詳細解析的文章就介紹到這了,更多相關(guān)JDK動態(tài)代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
maven創(chuàng)建spark項目的pom.xml文件配置demo
這篇文章主要為大家介紹了maven創(chuàng)建spark項目的pom.xml文件配置demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05Java數(shù)據(jù)結(jié)構(gòu)之鏈表詳解
本篇文章我們將講解一種新型的數(shù)據(jù)結(jié)構(gòu)—鏈表,鏈表是一種使用廣泛的通用數(shù)據(jù)結(jié)構(gòu),它可以用來作為實現(xiàn)棧,隊列等數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ).文中有非常詳細的介紹,需要的朋友可以參考下2021-05-05Java數(shù)組聲明、創(chuàng)建、初始化基礎(chǔ)
本文講述了Java數(shù)組的幾個相關(guān)的方面,講述了對Java數(shù)組的聲明、創(chuàng)建和初始化,并給出其對應的代碼2012-12-12