<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
靜態代理: 由我們開發者自己手動建立或者在程式執行前就已經存在的代理類,靜態代理通常只代理一個類,動態代理是代理一個介面下的多個實現類。
動態代理: 在程式執行時,運用java反射機制動態建立而成,靜態代理事先知道要代理的是什麼,而動態代理不知道要代理什麼東西,只有在執行時才知道,通常動態代理實現方式是通過實現 jdk 的 InvocationHandler 介面的 invoke 方法
需要代理的介面:
interface IBook { fun toBookName(name: String) }
class BookImpl : IBook { override fun toBookName(name: String) { println("書名|$name") } }
// TODO: 2020/11/25 靜態代理最大的特點就是,對於具體的代理提前宣告 class BookProxy( private val iBook: IBook, private var objStart: () -> Unit = { println("開始前的操作") }, private var objStop: () -> Unit = { println("結束時的操作") } ) : IBook { override fun toBookName(name: String) { //我們可以在具體的實現前做一些處理操作 objStart.invoke() iBook.toBookName(name) //具體的處理後做一些操作 objStop.invoke() } } fun main() { val bookProxy = BookProxy(BookImpl()) bookProxy.toBookName("Android&Petterp") }
靜態代理相對簡單,也有侷限性,對於代理類我們需要提前宣告,而且每一個代理都需要提前建立好。
class BookImpl : IBook { override fun toBookName(name: String) { println("測試輸出文字$name") } } /** 用於幫助代理的類 * BookImplHandler看起來很像一個比較奇怪的代理,實際上其只是用於幫助代理的類,我們最終生產的代理類會把呼叫發給它讓其處理。 * 代理類本身是通過 Proxy.newProxyInstance() 方法在執行時動態建立 * */ class BookImplHandler( private val book: IBook, private var objStart: () -> Unit = { println("開始前的操作") }, private var objStop: () -> Unit = { println("結束時的操作") } ) : InvocationHandler { // TODO: 2020/11/25 // (1)在invoke方法中接收可變長引數,在Kotlin語法中,陣列是array,可變長引數型別是vararg,型別不匹配。 // (2)Kotlin中陣列轉為可變長引數,通過前面加*符號。 // (3)如果方法沒有任何引數,args將為null,並將其傳播到Kotlin將導致NullPointerException. // 作為一種解決方法,使用*(args?:arrayOfNulls< Any>(0)),並在所描述的極端情況下選擇正確的部分並將其擴充套件為零引數. override fun invoke(proxy: Any, method: Method?, args: Array<out Any>?): Any? { objStart.invoke() val invoke = method?.invoke(book, *(args ?: emptyArray())) objStop.invoke() return invoke } } fun main() { val bookImplHandler = BookImplHandler(BookImplDynamic()) val iBook = Proxy.newProxyInstance( BookImpl::class.java.classLoader, BookImpl::class.java.interfaces, bookImplHandler ) as IBook iBook.toBookName("測試的文字") }
使用Retrofit時,當我們使用Retrofit.Builder().create()
時,傳入了我們的介面類,其 create 方法內部就使用了動態代理,從而生成了相應的代理類。
public <T> T create(final Class<T> service) { //判斷是否為介面 validateServiceInterface(service); return (T) //代理實現 Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] {service}, new InvocationHandler() { ... @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { //如果是object方法,則直接觸發 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } ... } }); }
如下所示,我們宣告一個註解,其應用於方法,並加入了一個Activity的擴充套件函數,用於對Activity重所有使用了該註解的方法進行onclick注入, 其方法內部使用反射+動態代理,從而實現。
@Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION) annotation class InjectClick(@IdRes val ids: IntArray) fun Activity.injectClicks() { javaClass.methods.asSequence().filter { it.isAnnotationPresent(InjectClick::class.java) }.forEach { it.isAccessible = true it.getAnnotation(InjectClick::class.java).ids.forEach { id -> findViewById<View>(id).apply { val clickProxy = Proxy.newProxyInstance( javaClass.classLoader, arrayOf(View.OnClickListener::class.java) ) { _, _, _ -> it.invoke(this@injectClicks) } as View.OnClickListener setOnClickListener(clickProxy) } } } }
動態代理的原始碼實現相對簡單,我們先進入Proxy.newProxyInstance
方法,一探究竟。
Proxy.newProxyInstance public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); //先克隆一個傳遞過來的代理物件 final Class<?>[] intfs = interfaces.clone(); //拿到生成的代理類,內部維護了一個map Class<?> cl = getProxyClass0(loader, intfs); //獲取代理類建構函式 final Constructor<?> cons = cl.getConstructor(constructorParams); //授予許可權 if (!Modifier.isPublic(cl.getModifiers())) { cons.setAccessible(true); } //反射建立代理類,並傳入使用者自己實現的中間層介面 return cons.newInstance(new Object[]{h}); }
具體如上述描述,然後我們自己生成一個代理類,來看看內部的呼叫:
fun writeFileProxy() { //IApple是我要代理的介面 val name = IApple::class.java.name + "Proxy()" val bytes = ProxyGenerator.generateProxyClass(name, arrayOf(IApple::class.java)) val fos = FileOutputStream("$name.class") fos.write(bytes) fos.close() }
範例程式碼:
public final class IAppleProxy() extends Proxy implements IApple { private static Method m3; public IAppleProxy__/* $FF was: IAppleProxy()*/(InvocationHandler var1) throws { super(var1); } public final void count() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { m3 = Class.forName("com.android.readbook.proxy.IApple").getMethod("count") } }
生成的代理類如上所示,其實現了我們的介面,並且代理了相應的所有方法,這裡做了一部分刪減。
觀察代理類具體方法的話,其具體實現裡,通過InvocationHandler
物件,也就是我們自己實現的輔助了,並呼叫其invoke方法, 以介面回撥的方式回撥到我們具體的實現處。整個過程比較容易理解,也並沒有什麼太高深的難度。
關於jdk中的動態代理,當我們呼叫 Proxy.newProxyInstance
時,傳入了一個當前類的classLoader以及要代理的介面陣列及實現了InvocationHandler 介面 的輔助類物件,其會在執行時在記憶體中生成一個代理類,這個代理類實現了我們的介面並接收 一個我們外部傳入的 InvocationHandler 輔助類物件,並在具體的方法實現位置通過呼叫輔助類的 invoke 方法,從而實現我們的介面方法代理。
到此這篇關於Java 靜態代理與動態代理解析的文章就介紹到這了,更多相關Java 靜態代理與動態代理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45