最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)代理,一直是比較困擾很多新人開(kāi)發(fā),但實(shí)際我們開(kāi)發(fā)中,小到寫(xiě)的某個(gè)工具類(lèi),大到經(jīng)常使用的Retrofit?其內(nèi)部都使用了動(dòng)態(tài)代理,所以這篇文章從基礎(chǔ)到源碼解析,以便簡(jiǎn)單理解靜態(tài)代理與Jdk中的動(dòng)態(tài)代理

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

動(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)方式是通過(guò)實(shí)現(xiàn) jdk 的 InvocationHandler 接口的 ??invoke?? 方法

一、代碼實(shí)踐

需要代理的接口:

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

靜態(tài)代理

class BookImpl : IBook {
? ? override fun toBookName(name: String) {
? ? ? ? println("書(shū)名|$name")
? ? }
}
// TODO: 2020/11/25 靜態(tài)代理最大的特點(diǎn)就是,對(duì)于具體的代理提前聲明
class BookProxy(
? ? private val iBook: IBook,
? ? private var objStart: () -> Unit = { println("開(kāi)始前的操作") },
? ? 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ì)于代理類(lèi)我們需要提前聲明,而且每一個(gè)代理都需要提前創(chuàng)建好。

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

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

/** 用于幫助代理的類(lèi)
?* BookImplHandler看起來(lái)很像一個(gè)比較奇怪的代理,實(shí)際上其只是用于幫助代理的類(lèi),我們最終生產(chǎn)的代理類(lèi)會(huì)把調(diào)用發(fā)給它讓其處理。
?* 代理類(lèi)本身是通過(guò) Proxy.newProxyInstance() 方法在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建
?* */
class BookImplHandler(
? ? private val book: IBook,
? ? private var objStart: () -> Unit = { println("開(kāi)始前的操作") },
? ? private var objStop: () -> Unit = { println("結(jié)束時(shí)的操作") }
) :
? ? InvocationHandler {
? ? // TODO: 2020/11/25
? ? // ?(1)在invoke方法中接收可變長(zhǎng)參數(shù),在Kotlin語(yǔ)法中,數(shù)組是array,可變長(zhǎng)參數(shù)類(lèi)型是vararg,類(lèi)型不匹配。
? ? // ?(2)Kotlin中數(shù)組轉(zhuǎn)為可變長(zhǎng)參數(shù),通過(guò)前面加*符號(hào)。
? ? // ?(3)如果方法沒(méi)有任何參數(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è)試的文本")
}

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

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

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

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

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

生成代理類(lèi)

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

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

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

四、總結(jié)

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

  • SpringBoot繼承LogStash實(shí)現(xiàn)日志收集的方法示例

    SpringBoot繼承LogStash實(shí)現(xiàn)日志收集的方法示例

    這篇文章主要介紹了SpringBoot繼承LogStash實(shí)現(xiàn)日志收集的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-05-05
  • 解析Linux系統(tǒng)中JVM內(nèi)存2GB上限的詳解

    解析Linux系統(tǒng)中JVM內(nèi)存2GB上限的詳解

    本篇文章是對(duì)Linux系統(tǒng)中JVM內(nèi)存2GB上限進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • Spring在多線程下保持事務(wù)的一致性的方法實(shí)現(xiàn)

    Spring在多線程下保持事務(wù)的一致性的方法實(shí)現(xiàn)

    當(dāng)Spring在多線程環(huán)境下運(yùn)行時(shí),確保事務(wù)一致性是非常重要的,本文主要介紹了Spring在多線程下保持事務(wù)的一致性的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • Java實(shí)現(xiàn)定時(shí)任務(wù)的方法詳解

    Java實(shí)現(xiàn)定時(shí)任務(wù)的方法詳解

    大家都用過(guò)鬧鐘,鬧鐘可以說(shuō)是一種定時(shí)任務(wù)。那么,在?Java?中,如何實(shí)現(xiàn)這樣的功能呢?即如何實(shí)現(xiàn)定時(shí)任務(wù)呢?本文就來(lái)詳細(xì)和大家聊聊
    2022-10-10
  • Java中常見(jiàn)對(duì)象映射工具的使用詳解

    Java中常見(jiàn)對(duì)象映射工具的使用詳解

    對(duì)象映射是 Java 開(kāi)發(fā)中的重要環(huán)節(jié),尤其在處理數(shù)據(jù)轉(zhuǎn)換時(shí),就需要將一種結(jié)構(gòu)的數(shù)據(jù)轉(zhuǎn)換為另一種結(jié)構(gòu)的數(shù)據(jù)(例如 DTO 和 Entity 之間),在這篇博客中,我們將認(rèn)識(shí)并解析幾種常用的對(duì)象映射工具,并提供詳細(xì)樣例,需要的朋友可以參考下
    2025-03-03
  • 詳解SpringBoot啟動(dòng)類(lèi)的掃描注解的用法及沖突原則

    詳解SpringBoot啟動(dòng)類(lèi)的掃描注解的用法及沖突原則

    這篇文章主要介紹了詳解SpringBoot啟動(dòng)類(lèi)的掃描注解的用法及沖突原則,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Java中Shiro安全框架的權(quán)限管理

    Java中Shiro安全框架的權(quán)限管理

    這篇文章主要介紹了Java中Shiro安全框架的權(quán)限管理,Apache?Shiro是Java的一個(gè)安全框架,Shiro可以非常容易的開(kāi)發(fā)出足夠好的應(yīng)用,其不僅可以用在JavaSE環(huán)境,也可以用在JavaEE環(huán)境,需要的朋友可以參考下
    2023-08-08
  • 詳解Spring Cloud Finchley版中Consul多實(shí)例注冊(cè)的問(wèn)題處理

    詳解Spring Cloud Finchley版中Consul多實(shí)例注冊(cè)的問(wèn)題處理

    這篇文章主要介紹了詳解Spring Cloud Finchley版中Consul多實(shí)例注冊(cè)的問(wèn)題處理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • SpringBoot整合Shiro思路(最新超詳細(xì))

    SpringBoot整合Shiro思路(最新超詳細(xì))

    這篇文章主要介紹了SpringBoot整合Shiro思路(最新超詳細(xì)),本文內(nèi)容比較長(zhǎng),通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • springboot接口多實(shí)現(xiàn)類(lèi)選擇性注入解決方案

    springboot接口多實(shí)現(xiàn)類(lèi)選擇性注入解決方案

    這篇文章主要為大家介紹了springboot接口多實(shí)現(xiàn)類(lèi)選擇性注入解決方案的四種方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03

最新評(píng)論

册亨县| 绍兴县| 沿河| 当阳市| 台东市| 调兵山市| 洛宁县| 平江县| 通渭县| 贵南县| 西和县| 莆田市| 陇川县| 淮北市| 眉山市| 扎兰屯市| 河池市| 汝州市| 南投县| 班戈县| 井研县| 双城市| 白玉县| 嫩江县| 察雅县| 承德市| 凤台县| 南岸区| 大安市| 城固县| 本溪| 洛扎县| 临泽县| 和林格尔县| 阿克苏市| 江油市| 神池县| 郎溪县| 菏泽市| 黄陵县| 响水县|