【问题标题】:How to create a dynamic proxy in Kotlin common code?如何在 Kotlin 常用代码中创建动态代理?
【发布时间】:2019-06-17 00:53:57
【问题描述】:

如果我在 JVM 上,我可以这样做:

object Playground {

    class DynamicInvocationHandler : InvocationHandler {

        @Throws(Throwable::class)
        override operator fun invoke(proxy: Any, method: Method, args: Array<Any>): Any {
            LOGGER.info("Invoked method: {}", method.name)

            return 42
        }

        companion object {

            private val LOGGER = LoggerFactory.getLogger(
                    DynamicInvocationHandler::class.java)
        }
    }

    @JvmStatic
    fun main(args: Array<String>) {
        val proxy = Proxy.newProxyInstance(
                Playground::class.java.classLoader,
                arrayOf<Class<*>>(MutableMap::class.java),
                DynamicInvocationHandler()) as MutableMap<String, String>

        proxy["foo"] = "bar"
    }
}

运行它将打印Invoked method: put。如何在 Kotlin common 项目中做这样的事情?

编辑:我不想在我的公共模块中使用 Java 中的任何东西。我知道常见的项目是如何运作的。相反,我感兴趣的是是否有基于 Kotlin 的解决方案。

编辑 2: 我不想代理 Map 类。我正在 JDK 中寻找类似 @​​987654325@ 的东西,我可以用它来代理 任何接口。很抱歉造成混乱。

【问题讨论】:

  • 您无法在公共模块中使用 Java 反射。因为这是一个 Java 特性,并且只在 Java 模块中可用。
  • 我编辑了我的答案。我知道这是一个 Java 特性,我明白什么是通用项目。我找不到基于 Kotlin 的解决方案,但这就是我要问的原因。

标签: kotlin proxy-pattern kotlin-multiplatform


【解决方案1】:

我认为简单的答案是 Kotlin 多平台反射不支持代理。在 java 应用程序中使用公共模块时,您可以使用 @KamiSempai 的 expect - actual 解决方案,但您需要找到 JS 和本机目标的替代方案。

【讨论】:

    【解决方案2】:

    可能期望/实际工厂应该解决问题。

    通用代码:

    interface ProxyMethod {
        val name: String
        // other properties
    }
    
    interface ProxyHandler {
        fun invoke(proxy: Any, method: ProxyMethod, args: Array<Any>): Any
    }
    
    expect object Logger {
        fun info(message: String, vararg arguments: Any)
    }
    
    expect object ProxyFactory {
        fun mutableMapWithProxy(handler: ProxyHandler): MutableMap<String, String>
    }
    
    private class ProxyHandlerImpl: ProxyHandler {
        override fun invoke(proxy: Any, method: ProxyMethod, args: Array<Any>): Any {
            Logger.info("Invoked method: {}", method.name)
            return 0
        }
    }
    
    object Common {
        fun doSomething() {
            val myMap = ProxyFactory.mutableMapWithProxy(ProxyHandlerImpl())
            myMap["foo"] = "bar"
        }
    }
    

    Java 代码:

    actual object Logger {
    
        private val instance = LoggerFactory.getLogger(
                DynamicInvocationHandler::class.java)
    
        actual fun info(message: String, vararg arguments: Any) {
            instance.info(message, *arguments)
        }
    }
    
    actual object ProxyFactory  {
        actual fun mutableMapWithProxy(handler: ProxyHandler): MutableMap<String, String> {
            return Proxy.newProxyInstance(
                    Playground::class.java.classLoader,
                    arrayOf<Class<*>>(MutableMap::class.java),
                    ProxyHandlerAdapter(handler)) as MutableMap<String, String>
        }
    }
    
    class ProxyHandlerAdapter(private val handler: ProxyHandler) : InvocationHandler {
    
        @Throws(Throwable::class)
        override operator fun invoke(proxy: Any, method: Method, args: Array<Any>): Any {
            return handler.invoke(proxy, methodToProxyMethod(method), args)
        }
    
        fun methodToProxyMethod(method: Method): ProxyMethod {
            // convert Method to ProxyMethod
        }
    }
    
    @JvmStatic
    fun main(args: Array<String>) {
        Common.doSomething()
    }
    

    很遗憾,我不知道有任何库可以简化这项工作,因此您应该为每个平台和界面手动完成这项工作。

    【讨论】:

    • 我不想代理Map。我正在寻找可以代理任何接口的代理解决方案。
    • 我明白你想要什么。但现在,如果不付出过多的努力,这是不可能的。也许不是,但它看起来像。我更新了我的答案以展示如何代理任何接口的方式。
    • 代码生成将简化工作。但是有人应该编写一个生成代码的库。
    • 小心java.lang.reflect.Proxy,它不会让你抛出任何类型的异常(它将包装它们以跟随JLS)jonnyzzz.com/blog/2018/11/22/proxy
    【解决方案3】:

    在当前的 Kotlin Native 版本中没有类似的功能。查看其他答案,它们似乎在期望/实际中具有实际类型,但实时代理的目的是在运行时提供类型并生成可以委托给的兼容实例。

    这就是改造之类的工作方式。接口定义不是源 gen,而是在内部代理。

    目前,据我所知,您需要执行 source-gen。那是给本地人的。不知道 JS。

    【讨论】:

    • 同意。在这种情况下,代码生成是实现类似于 Java 中的反射技巧的唯一方法。
    • 我不打算支持原生,但我看到 JS 也有 Proxy 实现!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-13
    • 2018-05-29
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多