Kotlin 学习笔记(三)—— Kotlin 的动态代理你会写吗?(下)

简介: Kotlin 学习笔记(三)—— Kotlin 的动态代理你会写吗?(下)

Java 动态代理实现


创建 Java 的动态代理需要使用 Proxy 类:

// code 8
java.lang.reflect.Proxy

调用它的 newProxyInstance 方法,就可以为某类创建一个代理类。例如给 Map 创建一个代理类:

// code 9
Map mapProxy = (Map) Proxy.newProxyInstance(
        HashMap.class.getClassLoader(),
        new Class[]{Map.class},
        new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                return null;
            }
        }
);


可以分析下这个方法,方法签名为:

// code 10
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  1. ClassLoader 类型的 loader 对象:被代理类的类加载器;
  2. Class 数组的 interfaces 对象:被代理的接口,对应于之前关键词中的 被代理的行为,这里传入的是接口而不是某个具体的类,所以表示行为;
  3. InvocationHandler 接口对象 h:代理的具体的行为,对应之前关键词中的 对行为的完全控制,这也是 Java 动态代理的核心;
  4. 返回的 Object 对象:对应之前关键词中的 代理对象。


所以,要实现一个动态代理大概需要下面几个步骤:

一、定义要被代理的行为 例如 code1 就是定义的要被代理的行为。

二、定义被代理的对象 就是实现了接口的类,例如实现了租房流程的房东,详见 code2。

三、建立代理关系 这个是代理的核心,需要一个实现了 InvocationHandler 接口的类:

// code 11
public class AgentHandler implements InvocationHandler {
    private Object target;
    public AgentHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行前");
        Object result = method.invoke(target, args);
        System.out.println("方法执行后");
        return result;
    }
}

接口中的 invoke 方法比较重要。方法体中的 Object 类型的 proxy 是最终生成的代理对象;Method 类型的 method 是被代理的方法;Object[] 数组类型的 args 是被代理方法执行所需要的参数。target 对象就是传入的代理类。在这个类中,可以在 invoke 方法前后插入我们需要执行的代码,这样做可以使得被代理类对象在执行任何方法时,都会执行我们插入的代码。例如,我们可以在 invoke 方法前后记录下时间戳,这样就可以得出被代理类对象执行的每一个方法的执行时长。

然后代理类利用 InvocationHandler 进行代理:

// code 12
public class HouseAgentSmart {
    private IRentHouse mHouseOwner;    // 房东,被代理类对象
    public HouseAgentSmart(IRentHouse houseOwner) {
        mHouseOwner = houseOwner;
        mHouseOwner = (IRentHouse) Proxy.newProxyInstance(
                houseOwner.getClass().getClassLoader(),
                new Class[]{IRentHouse.class},
                new AgentHandler(mHouseOwner)    // 将被代理对象传入
        );
    }
    public IRentHouse getAccess() {    // 用于返回代理对象
        return mHouseOwner;
    }
}

四、执行者调用

// code 13
IRentHouse smartAgent = new HouseAgentSmart(new HouseOwner()).getAccess();
        smartAgent.visitHouse();
        smartAgent.argueRent(400);
        smartAgent.signAgreement();


Kotlin 动态代理实现


上面是动态代理的 Java 实现,那么如何用 Kotlin 实现? 其实是一样的,只不过是编程语言的语法不同

// code 14
// IRentHouseKT.kt    1、首先还是定义接口
interface IRentHouseKT {
    // 带领租客看房
    fun visitHouse()
    // 讨价还价
    fun argueRent(rent : Int)
    // 签合同
    fun signAgreement()
}
// HouseOwnerKT.kt    2、然后是被代理类,实现接口
class HouseOwnerKT : IRentHouseKT {
    override fun visitHouse() {
        println("HouseOwner 带领看房,介绍房屋特点")
    }
    override fun argueRent(rent: Int) {
        println("HouseOwner 提出租金为:$rent")
    }
    override fun signAgreement() {
        println("HouseOwner 签合同")
    }
}
// AgentHandlerKT.kt    3、建立代理关系
class AgentHandlerKT : InvocationHandler {
    private var mTarget : Any? = null
    constructor(target : Any?) {
        this.mTarget = target
    }
    override fun invoke(proxy: Any?, method: Method, args: Array<out Any>?): Any? {
        println("方法执行前")
        // 因为传来的参数 args 是不确定的,所以用 *args.orEmpty() 传参
        val result = method.invoke(mTarget, *args.orEmpty())
        println("方法执行后")
        return result
    }
}
// HouseAgentSmartKT.kt    4、代理类,通过 AgentHandlerKT 得到代理关系
class HouseAgentSmartKT {
    var mHouseOwner : IRentHouseKT? = null
    constructor(houseOwner : IRentHouseKT) {
        mHouseOwner = houseOwner
        mHouseOwner = Proxy.newProxyInstance(
                houseOwner.javaClass.classLoader,
                arrayOf(IRentHouseKT::class.java),
                AgentHandlerKT(mHouseOwner)
        ) as IRentHouseKT
    }
}
// 调用方进行调用
val smartAgent = HouseAgentSmartKT(HouseOwnerKT()).mHouseOwner
smartAgent?.visitHouse()
smartAgent?.argueRent(500)
smartAgent?.signAgreement()


看到这里就有人要问了,咦?之前不是用 by 关键字就可以在 kotlin 中进行代理吗?为啥还需要像 Java 一样用 Proxy.newProxyInstance() 方法写代理的模式?这两种方式有什么区别?

首先,这两种方式都可以在 Kotlin 中实现代理模式,但适用的场景有所不同。 1、by 关键字实现的代理模式。使用起来更加方便,代理的粒度更小,可以根据代理类自身需要对某些被代理类中的方法进行重写; 2、Proxy.newProxyInstance() 方法实现的代理模式。实现比较繁琐,代理的粒度大,一旦代理,就是代理所有的方法。可以在原方法前后插入自己想要执行的代码,适用于埋点记录 log 日志、记录方法执行用时等。


参考文献


  1. Java动态代理——框架中的应用场景和基本原理
  2. 当Kotlin邂逅设计模式之代理模式(二)

还没看够?欢迎来逛逛我的公众号。搜索:修之竹也欢迎知乎关注 修之竹~

ps. 赠人玫瑰,手留余香。欢迎转发分享加关注,你的认可是我继续创作的精神源泉。

目录
相关文章
|
11月前
|
分布式计算 Java 数据安全/隐私保护
Kotlin 学习笔记(二)—— 数据类、枚举类、循环、常用集合及操作符的写法(下 )
Kotlin 学习笔记(二)—— 数据类、枚举类、循环、常用集合及操作符的写法(下)
58 0
|
安全 Java 编译器
kotlin中学习笔记——null
指定一个变量可null是通过在它的类型后面加?号,如 val a: String? = null 复制代码 (注意这个Int也可为空是因为在kotlin中一切都是对象,包括在java中的基本类型) 一个可null类型,没有进行检查前不能使用,如下代码会编译出错 val a: String? = null a.subString(2)
308 0
|
11月前
|
缓存 API Android开发
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(下)
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(下)
138 0
|
11月前
|
缓存 Java Kotlin
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(上)
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(上)
92 0
|
11月前
|
存储 缓存 Android开发
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(下)
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(下)
150 0
|
11月前
|
存储 缓存 人工智能
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(上)
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(上)
71 0
|
11月前
|
API Android开发 Kotlin
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(下)
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(下)
55 0
|
11月前
|
安全 Kotlin
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(上)
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(上)
66 0
|
11月前
|
安全 调度 数据库
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(下)
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(下)
58 0
|
11月前
|
Java Go Android开发
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(上)
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(上)
76 0