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 {