如何在Kotlin通用代码中创建动态代理?

时间:2019-01-23 12:20:25

标签: kotlin proxy-pattern kotlin-multiplatform

如果我在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 常见项目中做类似的事情?

编辑:我没有在通用模块中尝试使用Java中的任何内容。我知道常见项目的运作方式。相反,我感兴趣的是是否有基于Kotlin的解决方案。

编辑2:我不是在尝试代理Map类。我正在JDK中寻找类似Proxy的东西,可以用来代理任何接口。抱歉造成混乱。

3 个答案:

答案 0 :(得分: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()
}

不幸的是,我不知道有哪个库可以简化这项工作,因此您应该手动为每个平台和界面执行此操作。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

这就是翻新之类的工作方式。接口定义不是内部源,而是内部代理。

就目前而言,据我所知,您需要做source-gen。那是本地人的。不确定JS。