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. 赠人玫瑰,手留余香。欢迎转发分享加关注,你的认可是我继续创作的精神源泉。

目录
相关文章
|
2月前
|
Java 开发者 Kotlin
Kotlin学习笔记- 类与构造器
本篇笔记详细介绍了Kotlin中的类与构造器,包括类的基本概念、主构造器与次构造器的区别、构造器中参数的使用规则、类的继承以及构造器在继承中的应用等。通过具体示例,解释了如何在类中定义属性、实现构造逻辑,并探讨了Kotlin类的继承机制和Any类的作用。此外,还简要介绍了包的概念及其在组织代码中的作用。适合初学者深入理解Kotlin面向对象编程的核心概念。
35 3
|
2月前
|
Java 编译器 Kotlin
Kotlin学习笔记 - 数据类型
《Kotlin学习笔记 - 数据类型》是Kotlin编程语言学习系列的一部分,专注于Kotlin中的数据类型,包括布尔型、数字型(整型和浮点型)、字符型及字符串型,详述了各类型的定义、使用方法及相互间的转换规则。适合初学者快速掌握Kotlin基础语法。
29 3
|
2月前
|
安全 IDE Java
Kotlin 学习笔记- 空类型和智能类型转换
Kotlin 学习笔记聚焦于空类型和智能类型转换,深入解析非空与可空类型、安全调用操作符、Elvis 运算符、非空断言运算符及智能类型转换等内容,助你高效掌握 Kotlin 语言特性,避免 NullPointException 异常,提升代码质量。
31 2
|
2月前
|
Java 编译器 Kotlin
Kotlin学习笔记 - 数据类型
Kotlin学习笔记 - 数据类型
46 4
|
2月前
|
Java 开发者 Kotlin
Kotlin学习笔记- 类与构造器
Kotlin学习笔记- 类与构造器
32 3
|
2月前
|
设计模式 Java Kotlin
Kotlin学习笔记 - 改良设计模式 - 迭代器模式
Kotlin学习笔记 - 改良设计模式 - 迭代器模式
30 2
|
2月前
|
安全 IDE Java
Kotlin 学习笔记- 空类型和智能类型转换
Kotlin 学习笔记- 空类型和智能类型转换
50 2
|
2月前
|
设计模式 JavaScript Scala
Kotlin学习笔记 - 改良设计模式 - 责任链模式
Kotlin学习笔记 - 改良设计模式 - 责任链模式
45 0
|
2月前
|
设计模式 Java Kotlin
Kotlin 学习笔记- 改良设计模式 - 装饰者模式
Kotlin 学习笔记- 改良设计模式 - 装饰者模式
27 0
|
缓存 API Android开发
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(下)
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(下)
172 0