欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?靜態(tài)代理與動(dòng)態(tài)代理解析

 更新時(shí)間:2022年02月18日 12:12:11   作者:Petterpx  
這篇文章主要介紹了Java?靜態(tài)代理與動(dòng)態(tài)代理解析,關(guān)于靜態(tài)代理與動(dòng)態(tài)代理,一直是比較困擾很多新人開發(fā),但實(shí)際我們開發(fā)中,小到寫的某個(gè)工具類,大到經(jīng)常使用的Retrofit?其內(nèi)部都使用了動(dòng)態(tài)代理,所以這篇文章從基礎(chǔ)到源碼解析,以便簡(jiǎn)單理解靜態(tài)代理與Jdk中的動(dòng)態(tài)代理

靜態(tài)代理: 由我們開發(fā)者自己手動(dòng)創(chuàng)建或者在程序運(yùn)行前就已經(jīng)存在的代理類,靜態(tài)代理通常只代理一個(gè)類,動(dòng)態(tài)代理是代理一個(gè)接口下的多個(gè)實(shí)現(xiàn)類。

動(dòng)態(tài)代理: 在程序運(yùn)行時(shí),運(yùn)用java反射機(jī)制動(dòng)態(tài)創(chuàng)建而成,靜態(tài)代理事先知道要代理的是什么,而動(dòng)態(tài)代理不知道要代理什么東西,只有在運(yùn)行時(shí)才知道,通常動(dòng)態(tài)代理實(shí)現(xiàn)方式是通過實(shí)現(xiàn) jdk 的 InvocationHandler 接口的 ??invoke?? 方法

一、代碼實(shí)踐

需要代理的接口:

interface IBook {
? ? fun toBookName(name: String)
}

靜態(tài)代理

class BookImpl : IBook {
? ? override fun toBookName(name: String) {
? ? ? ? println("書名|$name")
? ? }
}
// TODO: 2020/11/25 靜態(tài)代理最大的特點(diǎn)就是,對(duì)于具體的代理提前聲明
class BookProxy(
? ? private val iBook: IBook,
? ? private var objStart: () -> Unit = { println("開始前的操作") },
? ? private var objStop: () -> Unit = { println("結(jié)束時(shí)的操作") }
) : IBook {
? ? override fun toBookName(name: String) {
? ? ? ? //我們可以在具體的實(shí)現(xiàn)前做一些處理操作
? ? ? ? objStart.invoke()
? ? ? ? iBook.toBookName(name)
? ? ? ? //具體的處理后做一些操作
? ? ? ? objStop.invoke()
? ? }
}

fun main() {
? ? val bookProxy = BookProxy(BookImpl())
? ? bookProxy.toBookName("Android&Petterp")
}

靜態(tài)代理相對(duì)簡(jiǎn)單,也有局限性,對(duì)于代理類我們需要提前聲明,而且每一個(gè)代理都需要提前創(chuàng)建好。

動(dòng)態(tài)代理

class BookImpl : IBook {
? ? override fun toBookName(name: String) {
? ? ? ? println("測(cè)試輸出文本$name")
? ? }
}

/** 用于幫助代理的類
?* BookImplHandler看起來很像一個(gè)比較奇怪的代理,實(shí)際上其只是用于幫助代理的類,我們最終生產(chǎn)的代理類會(huì)把調(diào)用發(fā)給它讓其處理。
?* 代理類本身是通過 Proxy.newProxyInstance() 方法在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建
?* */
class BookImplHandler(
? ? private val book: IBook,
? ? private var objStart: () -> Unit = { println("開始前的操作") },
? ? private var objStop: () -> Unit = { println("結(jié)束時(shí)的操作") }
) :
? ? InvocationHandler {
? ? // TODO: 2020/11/25
? ? // ?(1)在invoke方法中接收可變長參數(shù),在Kotlin語法中,數(shù)組是array,可變長參數(shù)類型是vararg,類型不匹配。
? ? // ?(2)Kotlin中數(shù)組轉(zhuǎn)為可變長參數(shù),通過前面加*符號(hào)。
? ? // ?(3)如果方法沒有任何參數(shù),args將為null,并將其傳播到Kotlin將導(dǎo)致NullPointerException.
? ? // ?作為一種解決方法,使用*(args?:arrayOfNulls< Any>(0)),并在所描述的極端情況下選擇正確的部分并將其擴(kuò)展為零參數(shù).
? ? 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("測(cè)試的文本")
}

二、常見的動(dòng)態(tài)代理場(chǎng)景

Retrofit中的動(dòng)態(tài)代理

使用Retrofit時(shí),當(dāng)我們使用Retrofit.Builder().create() 時(shí),傳入了我們的接口類,其 ??create?? 方法內(nèi)部就使用了動(dòng)態(tài)代理,從而生成了相應(yīng)的代理類。

public <T> T create(final Class<T> service) {
? //判斷是否為接口
? validateServiceInterface(service);
? return (T)
? ? ? //代理實(shí)現(xiàn)
? ? ? Proxy.newProxyInstance(
? ? ? ? ? service.getClassLoader(),
? ? ? ? ? new Class<?>[] {service},
? ? ? ? ? new InvocationHandler() {
? ? ? ? ? ? ...

? ? ? ? ? ? @Override
? ? ? ? ? ? public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
? ? ? ? ? ? ? ? throws Throwable {
? ? ? ? ? ? ? //如果是object方法,則直接觸發(fā)
? ? ? ? ? ? ? if (method.getDeclaringClass() == Object.class) {
? ? ? ? ? ? ? ? return method.invoke(this, args);
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ...
? ? ? ? ? ? }
? ? ? ? ? });
}

使用動(dòng)態(tài)代理實(shí)現(xiàn) onClick注入

如下所示,我們聲明一個(gè)注解,其應(yīng)用于方法,并加入了一個(gè)Activity的擴(kuò)展函數(shù),用于對(duì)Activity重所有使用了該注解的方法進(jìn)行onclick注入, 其方法內(nèi)部使用反射+動(dòng)態(tài)代理,從而實(shí)現(xiàn)。

@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)
? ? ? ? ? ? }
? ? ? ? }
? ? }
}

三、源碼探索 Jdk 中的動(dòng)態(tài)代理

動(dòng)態(tài)代理的源碼實(shí)現(xiàn)相對(duì)簡(jiǎn)單,我們先進(jìn)入Proxy.newProxyInstance 方法,一探究竟。

Proxy.newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Class<?>[] interfaces,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? InvocationHandler h)
? ? ? ? ? ? throws IllegalArgumentException {
? ? ? ? Objects.requireNonNull(h);
? ? ? ? //先克隆一個(gè)傳遞過來的代理對(duì)象
? ? ? ? final Class<?>[] intfs = interfaces.clone();
? ? ? ? //拿到生成的代理類,內(nèi)部維護(hù)了一個(gè)map
? ? ? ? Class<?> cl = getProxyClass0(loader, intfs);
? ? ? ? //獲取代理類構(gòu)造函數(shù)
? ? ? ? final Constructor<?> cons = cl.getConstructor(constructorParams);
? ? ? ? //授予權(quán)限
? ? ? ? if (!Modifier.isPublic(cl.getModifiers())) {
? ? ? ? ? ? cons.setAccessible(true);
? ? ? ? }
? ? ? ? //反射創(chuàng)建代理類,并傳入用戶自己實(shí)現(xiàn)的中間層接口
? ? ? ? return cons.newInstance(new Object[]{h});
}

具體如上述描述,然后我們自己生成一個(gè)代理類,來看看內(nèi)部的調(diào)用:

生成代理類

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")
? ? }
}

生成的代理類如上所示,其實(shí)現(xiàn)了我們的接口,并且代理了相應(yīng)的所有方法,這里做了一部分刪減。

觀察代理類具體方法的話,其具體實(shí)現(xiàn)里,通過InvocationHandler對(duì)象,也就是我們自己實(shí)現(xiàn)的輔助了,并調(diào)用其invoke方法, 以接口回調(diào)的方式回調(diào)到我們具體的實(shí)現(xiàn)處。整個(gè)過程比較容易理解,也并沒有什么太高深的難度。

四、總結(jié)

關(guān)于jdk中的動(dòng)態(tài)代理,當(dāng)我們調(diào)用 Proxy.newProxyInstance 時(shí),傳入了一個(gè)當(dāng)前類的classLoader以及要代理的接口數(shù)組及實(shí)現(xiàn)了InvocationHandler 接口 的輔助類對(duì)象,其會(huì)在運(yùn)行時(shí)在內(nèi)存中生成一個(gè)代理類,這個(gè)代理類實(shí)現(xiàn)了我們的接口并接收 一個(gè)我們外部傳入的 InvocationHandler 輔助類對(duì)象,并在具體的方法實(shí)現(xiàn)位置通過調(diào)用輔助類的 ??invoke?? 方法,從而實(shí)現(xiàn)我們的接口方法代理。

到此這篇關(guān)于Java 靜態(tài)代理與動(dòng)態(tài)代理解析的文章就介紹到這了,更多相關(guān)Java 靜態(tài)代理與動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring中propagation的7種事務(wù)配置及說明

    Spring中propagation的7種事務(wù)配置及說明

    這篇文章主要介紹了Spring中propagation的7種事務(wù)配置及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • SpringBoot淺析安全管理之OAuth2框架

    SpringBoot淺析安全管理之OAuth2框架

    安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會(huì)發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會(huì)出現(xiàn)問題,這篇文章主要介紹了SpringBoot安全管理OAuth2框架的使用
    2022-08-08
  • jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)

    jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)

    本篇文章主要介紹了jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)。具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-04-04
  • Java如何使用Optional與Stream取代if判空邏輯(JDK8以上)

    Java如何使用Optional與Stream取代if判空邏輯(JDK8以上)

    這篇文章主要給大家介紹了關(guān)于Java如何使用Optional與Stream取代if判空邏輯(JDK8以上)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • JAVA提高第七篇 類加載器解析

    JAVA提高第七篇 類加載器解析

    這篇文章主要為大家詳細(xì)介紹了JAVA提高第七篇類加載器的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • @JsonFormat?和?@DateTimeFormat?時(shí)間格式化注解(場(chǎng)景示例代碼)

    @JsonFormat?和?@DateTimeFormat?時(shí)間格式化注解(場(chǎng)景示例代碼)

    這篇文章主要介紹了@JsonFormat和@DateTimeFormat時(shí)間格式化注解,本文通過場(chǎng)景示例代碼詳解給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • Java生成唯一id的幾種實(shí)現(xiàn)方式

    Java生成唯一id的幾種實(shí)現(xiàn)方式

    本文主要介紹了Java生成唯一id的幾種實(shí)現(xiàn)方式,主要介紹了5種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • java中文傳值亂碼問題的解決方法

    java中文傳值亂碼問題的解決方法

    這篇文章主要為大家詳細(xì)介紹了java中文傳值亂碼問題的解決方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • SpringBoot使用Redis實(shí)現(xiàn)分布式緩存

    SpringBoot使用Redis實(shí)現(xiàn)分布式緩存

    這篇文章主要介紹了SpringBoot redis分布式緩存實(shí)現(xiàn)過程解析,文中通過示例代碼解析的非常詳細(xì),感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • java通過URLClassLoader類加載器加載外部jar代碼示例

    java通過URLClassLoader類加載器加載外部jar代碼示例

    ClassLoader翻譯過來就是類加載器,普通的java開發(fā)者其實(shí)用到的不多,但對(duì)于某些框架開發(fā)者來說卻非常常見,下面這篇文章主要給大家介紹了關(guān)于java通過URLClassLoader類加載器加載外部jar的相關(guān)資料,需要的朋友可以參考下
    2024-01-01

最新評(píng)論