Перехват функций приостановки с использованием динамического прокси Java - PullRequest
0 голосов
/ 02 марта 2019

В следующем коде для перехвата функций приостановки используется функция [sProxy].

Печатается:

add[1, 2] called
3

Код

typealias SInvocation = suspend () -> Any?
typealias SInterceptor =
    suspend (method: Method, args: List<Any?>, invocation: SInvocation) -> Any?

interface Calculator {
    suspend fun add(a: Int, b: Int): Int
}

class CalculatorImpl : Calculator {
    override suspend fun add(a: Int, b: Int) = a + b
}

val Printer: SInterceptor = { method, args, invocation ->
    println("${method.name}$args called")
    invocation()
}

fun main() = runBlocking {
    val calculator = sProxy(Calculator::class.java, CalculatorImpl(), Printer)
    println(calculator.add(1, 2))
}

Следующий код показываетрабочая реализация функции [sProxy]:

interface SFunction {
    suspend fun invoke(): Any?
}

val SRemover: Method = SFunction::class.java.methods[0]

typealias SInvoker = suspend (method: Method, args: List<Any?>) -> Any?

fun Method.sInvoke(
    args: List<Any?>, continuation: Continuation<*>, invoker: SInvoker
): Any? = SRemover.invoke(object : SFunction {
    override suspend fun invoke(): Any? = invoker(this@sInvoke, args)
}, continuation)

suspend fun Method.sInvoke(implementation: Any, args: List<Any?>): Any? =
    suspendCoroutineUninterceptedOrReturn { continuation ->
        sInvoke(args, continuation) { _, _ ->
            invoke(implementation, *args.toTypedArray(), continuation)
        }
    }

/** Creates a proxy for [sContract] that intercepts calls to [implementation]. */
@Suppress("UNCHECKED_CAST")
fun <C : Any> sProxy(
    sContract: Class<C>, implementation: C, interceptor: SInterceptor
): C = Proxy.newProxyInstance(
    sContract.classLoader, arrayOf(sContract)
) { _, method, args ->
    method.sInvoke(
        args.take(args.size - 1), args.last() as Continuation<*>
    ) { _, params ->
        interceptor(method, params) { method.sInvoke(implementation, params) }
    }
} as C

Итак, теперь возникают вопросы: - Это хорошее / проблемное решение?- Есть ли лучшее решение?

Буду признателен за обратную связь, может быть, даже от члена основной команды Kotlin?

Полный код можно найти по адресу https://github.com/softappeal/yass/blob/master/kotlin/yass/test/ch/softappeal/yass/demo/DynamicProxyDemo.kt

Я использую его в своей сервисной инфраструктуре для реализации неблокирующих вызовов с Ktor.

...