Kotlin在java.lang.reflect.Proxy对象上调用扩展函数时的奇怪行为

Kotlin在java.lang.reflect.Proxy对象上调用扩展函数时的奇怪行为,java,kotlin,reflection,dynamic-proxy,extension-function,Java,Kotlin,Reflection,Dynamic Proxy,Extension Function,今天我在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() } }

今天我在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()

}

问题:

为什么在第一个示例中,代理只为
bark3次
调用而不为单独的
bark
调用,但在第二个示例中,它不是为
bark5次
调用的,而是为每次
bark
调用调用的?

这就是所谓的自调用,也是基于代理的AOP中大量错误的来源(例如Spring的
@Transactional
@Cacheable

您的
Proxy
Dog
充当底层
doimpl
实例的装饰器。当主方法调用
proxyDog.bark5Times()
时,扩展方法在代理对象上连续调用
bark()
五次,其中包含建议,从而打印“Proxy invoked!”消息


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

您的困惑源于对扩展函数的误解。它们在JVM中编译为静态方法,并且不会神奇地“注入”到类中。当您意识到它们看起来像Java代码时,问题变得显而易见:

// Kotlin
fun Dog.bark5Times() = (0 until 5).forEach { bark() }

// equivalent in Java
public static void dogBark5Times(Dog dog){ 
    for(i = 0; i < 5; i++){ dog.bark(); }  // in this case dog is proxy so log is shown
}
//Kotlin
有趣的狗。bark5Times()=(0到5)。forEach{bark()}
//Java中的等效
公共静态无效狗吠5次(狗吠){
对于(i=0;i<5;i++){dog.bark();}//在本例中,dog是代理,因此显示日志
}

提示与问题无关,而不是
(0到5)。对于每个{…}
您可以使用内置函数:
重复(5){…}
@Pawel谢谢,我将把建议应用到问题中,以获得更为惯用的代码。
// Kotlin
fun Dog.bark5Times() = (0 until 5).forEach { bark() }

// equivalent in Java
public static void dogBark5Times(Dog dog){ 
    for(i = 0; i < 5; i++){ dog.bark(); }  // in this case dog is proxy so log is shown
}