首页 > 解决方案 > 在 java.lang.reflect.Proxy 对象上调用扩展函数时 Kotlin 的奇怪行为

问题描述

今天我在 Kotlin 中玩了一些java.lang.reflect.Proxy,我对这种行为感到惊讶:

import java.lang.reflect.Proxy

interface Dog {
  fun bark()
  fun bark3Times()
}

class DogImpl : Dog {
  override fun bark() = println("Bark!")
  override fun bark3Times() = repeat(3) { bark() }
}

fun Dog.bark5Times() = repeat(5) { bark() }

fun main(args: Array<String>) {

  val classLoader = Dog::class.java.classLoader

  val realDog: Dog = DogImpl()

  val proxyDog: Dog = Proxy.newProxyInstance(
    classLoader,
    arrayOf(Dog::class.java)
  ) { _, method, _ ->

    println("Proxy invoked! Method = ${method.name}")
    method.invoke(realDog)

  } as Dog

  println("--- Dog barking 3 times ---")
  proxyDog.bark3Times()

  println()
  println("--- Dog barking 5 times ---")
  proxyDog.bark5Times()

}

输出:

--- Dog barking 3 times ---
Proxy invoked! Method = bark3Times
Bark!
Bark!
Bark!

--- Dog barking 5 times ---
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!
Proxy invoked! Method = bark
Bark!

问题:

为什么在第一个示例中仅bark3Times调用代理而不是单独bark调用,但在第二个示例中不调用它bark5Times,但每次bark调用都调用这个时间?

标签: javakotlinreflectiondynamic-proxyextension-function

解决方案


这就是所谓的自调用,并且是基于代理的 AOP(例如 Spring@Transactional@Cacheable)中 bug 的重要来源。

Proxy Dog正在充当底层DogImpl实例的装饰器。当您的 main 方法调用proxyDog.bark5Times()时,扩展方法在代理对象上bark()连续调用五次,其中包含建议并因此打印您的“代理调用!” 信息。

但是,当您调用 时bark3Times(),该调用会命中代理(打印日志消息!)......然后DogImpl实例this.bark() 直接调用自身3 次,而不通过代理。


推荐阅读