diff --git a/spring-aop/src/test/kotlin/org/springframework/aop/framework/CglibAopProxyKotlinTests.kt b/spring-aop/src/test/kotlin/org/springframework/aop/framework/CglibAopProxyKotlinTests.kt
index 10e632abd3..0489d68bb0 100644
--- a/spring-aop/src/test/kotlin/org/springframework/aop/framework/CglibAopProxyKotlinTests.kt
+++ b/spring-aop/src/test/kotlin/org/springframework/aop/framework/CglibAopProxyKotlinTests.kt
@@ -19,6 +19,7 @@ package org.springframework.aop.framework
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test
+import java.time.LocalDateTime
/**
* Tests for Kotlin support in [CglibAopProxy].
@@ -48,6 +49,13 @@ class CglibAopProxyKotlinTests {
assertThatThrownBy { proxy.checkedException() }.isInstanceOf(CheckedException::class.java)
}
+ @Test // gh-35487
+ fun jvmDefault() {
+ val proxyFactory = ProxyFactory()
+ proxyFactory.setTarget(AddressRepo())
+ proxyFactory.proxy
+ }
+
open class MyKotlinBean {
@@ -63,4 +71,24 @@ class CglibAopProxyKotlinTests {
}
class CheckedException() : Exception()
+
+ open class AddressRepo(): CrudRepo
+
+ interface CrudRepo {
+ fun save(e: E): E {
+ return e
+ }
+ fun delete(id: ID): Long {
+ return 0L
+ }
+ }
+
+ data class Address(
+ val id: Int = 0,
+ val street: String,
+ val version: Int = 0,
+ val createdAt: LocalDateTime? = null,
+ val updatedAt: LocalDateTime? = null,
+ )
+
}
diff --git a/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java b/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java
index 3acd2fd69f..f9f5275cac 100644
--- a/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java
+++ b/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java
@@ -1282,29 +1282,32 @@ public class Enhancer extends AbstractClassGenerator {
Signature bridgeTarget = (Signature) bridgeToTarget.get(method.getSignature());
if (bridgeTarget != null) {
// checkcast each argument against the target's argument types
- for (int i = 0; i < bridgeTarget.getArgumentTypes().length; i++) {
+ Type[] argTypes = method.getSignature().getArgumentTypes();
+ Type[] targetTypes = bridgeTarget.getArgumentTypes();
+ for (int i = 0; i < targetTypes.length; i++) {
e.load_arg(i);
- Type target = bridgeTarget.getArgumentTypes()[i];
- if (!target.equals(method.getSignature().getArgumentTypes()[i])) {
+ Type argType = argTypes[i];
+ Type target = targetTypes[i];
+ if (!target.equals(argType)) {
+ if (!TypeUtils.isPrimitive(target)) {
+ e.box(argType);
+ }
e.checkcast(target);
}
}
e.invoke_virtual_this(bridgeTarget);
+ // Not necessary to cast if the target & bridge have the same return type.
Type retType = method.getSignature().getReturnType();
- // Not necessary to cast if the target & bridge have
- // the same return type.
- // (This conveniently includes void and primitive types,
- // which would fail if casted. It's not possible to
- // covariant from boxed to unbox (or vice versa), so no having
- // to box/unbox for bridges).
- // TODO: It also isn't necessary to checkcast if the return is
- // assignable from the target. (This would happen if a subclass
- // used covariant returns to narrow the return type within a bridge
- // method.)
- if (!retType.equals(bridgeTarget.getReturnType())) {
- e.checkcast(retType);
+ Type target = bridgeTarget.getReturnType();
+ if (!target.equals(retType)) {
+ if (!TypeUtils.isPrimitive(target)) {
+ e.unbox(retType);
+ }
+ else {
+ e.checkcast(retType);
+ }
}
}
else {