Affects: v5.3.4
In kotlin, non-nullable Int is compiled to java primitive type int, this cause the condition candidateParameter.equals(genericParameter.toClass()
always evaluates to false
. so org.springframework.core.BridgeMethodResolver#findBridgedMethod
fails to find the correct bridge method.
import org.springframework.stereotype.Repository
import org.springframework.transaction.annotation.Transactional
interface GenericInterface<ID> {
fun delete(id: ID)
}
abstract class AbstractGenericClass<ID> : GenericInterface<ID> {
override fun delete(id: ID) {
}
}
@Repository
class GenericRepository : AbstractGenericClass<Int>() {
@Transactional
override fun delete(
id: Int
) {
error("gotcha")
}
}
the above "GenericRepository" is compiled to
@Repository
@Metadata(
mv = {1, 4, 0},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0010\b\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0002\b\u0017\u0018\u00002\b\u0012\u0004\u0012\u00020\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0003J\u0010\u0010\u0004\u001a\u00020\u00052\u0006\u0010\u0006\u001a\u00020\u0002H\u0017¨\u0006\u0007"},
d2 = {"Lcom/cpvsn/crud/demo/feat/generic/GenericRepository;", "Lcom/cpvsn/crud/demo/feat/generic/AbstractGenericClass;", "", "()V", "delete", "", "id", "crud-kit.crud-kit-demo.main"}
)
public class GenericRepository extends AbstractGenericClass {
@Transactional
public void delete(int id) {
String var2 = "gotcha";
boolean var3 = false;
throw (Throwable)(new IllegalStateException(var2.toString()));
}
// $FF: synthetic method
// $FF: bridge method
public void delete(Object var1) {
this.delete(((Number)var1).intValue());
}
}
Due to this issue, the @Transactional
on delete(id: Int)
method have no effects at all.
I'm not sure if this project is expected to be compatible with Kotlin code. If it is, then this is a bug, I think I can try to submit a PR to fix it.
Comment From: sdeleuze
@jhoeller I have little experience with bridge methods, but based on my initial findings with this test, my current thinking is that we could add Kotlin specific code path in BridgeMethodResolver#isResolvedTypeMatch
.
Instead of just doing Type[] genericParameters = genericMethod.getGenericParameterTypes()
, we should maybe use KClass<T>.javaObjectType
in order to get a type that will be compatible with the rest f the logic. In the use case we have here, java.lang.Integer
would be returned for both Kotlin Int
and Int?
types. But I am not sure KClass<T>.javaObjectType
is exposed in Java, to be checked with Kotlin team.
I think you mentioned another potential way to fix that, but I can't remember the details. Any thoughts?
@noob9527 Since you mentioned a PR, any thoughts on what could look like the fix?
Comment From: noob9527
@sdeleuze
I think ResolvableType.forMethodParameter(genericMethod, i, declaringClass);
will always return a wrapper type.
so we can just wrap candidateParameter
to its wrapper type before comparing.