How to create a dynamic proxy in Kotlin common code?

我的梦境 提交于 2019-12-10 11:14:23

问题


If I'm on the JVM I can do this:

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

and running this will print Invoked method: put. How can I do something like this in a Kotlin common project?

Edit: I'm not trying to use anything from Java in my common module. I know how common projects work. What I'm interested in instead is whether there is a Kotlin-based solution for this or not.

Edit 2: I'm not trying to proxy the Map class. I'm looking for something like Proxy in the JDK which I can use to proxy any interface. Sorry for the confusion.


回答1:


I think the simple answer is that Kotlin Multi Platform reflection doesn't support proxies. You can use @KamiSempai's expect - actual solution when using the common module in a java application but you will need to find alternatives for JS and native targets.




回答2:


Probably Expect/Actual Factory should solve the problem.

Common Code:

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 Code:

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()
}

Unfortunately I don't know any library that simplify this work so you should do this by hand for each platform and interface.




回答3:


There is no equivalent for this in current Kotlin Native versions. Looking at the other answers, they seem to have actual types in the expect/actuals, but the purpose of a live proxy is to supply a type at runtime and generate a compatible instance that can be delegated to.

This is how things like Retrofit work. Rather than source gen, the interface definitions are proxied internally.

For now, you'll need to do source-gen, as far as I know. That's for native. Not sure about JS.



来源:https://stackoverflow.com/questions/54327143/how-to-create-a-dynamic-proxy-in-kotlin-common-code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!