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

Posted

技术标签:

【中文标题】如何在 Kotlin 常用代码中创建动态代理?【英文标题】:How to create a dynamic proxy in Kotlin common code? 【发布时间】: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 的解决方案,但这就是我要问的原因。 找到discuss.kotlinlang.org/t/dynamic-proxy/17802讨论这个话题。 【参考方案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 实现!

以上是关于如何在 Kotlin 常用代码中创建动态代理?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ReactJS 中创建动态表?

如何在 react.js 中创建动态网格?

如何在 Android Studio 中创建动态 ListView

如何在sql中创建动态where子句?

2017/12/30 GUI和动态代理

如何在 C# 中创建动态 Web api 客户端?