Browse Source

Introduce KotlinDetector#hasSerializableAnnotation

This commit introduces a KotlinDetector#hasSerializableAnnotation
utility method designed to detect types annotated with
`@Serializable` at type or generics level.

See gh-35761
pull/34993/merge
Sébastien Deleuze 1 month ago
parent
commit
a68d60768e
  1. 32
      spring-core/src/main/java/org/springframework/core/KotlinDetector.java
  2. 20
      spring-core/src/test/kotlin/org/springframework/core/KotlinDetectorTests.kt

32
spring-core/src/main/java/org/springframework/core/KotlinDetector.java

@ -38,6 +38,8 @@ public abstract class KotlinDetector { @@ -38,6 +38,8 @@ public abstract class KotlinDetector {
private static final @Nullable Class<? extends Annotation> KOTLIN_JVM_INLINE;
private static final @Nullable Class<? extends Annotation> KOTLIN_SERIALIZABLE;
private static final @Nullable Class<?> KOTLIN_COROUTINE_CONTINUATION;
// For ConstantFieldFeature compliance, otherwise could be deduced from kotlinMetadata
@ -49,6 +51,7 @@ public abstract class KotlinDetector { @@ -49,6 +51,7 @@ public abstract class KotlinDetector {
ClassLoader classLoader = KotlinDetector.class.getClassLoader();
Class<?> metadata = null;
Class<?> jvmInline = null;
Class<?> serializable = null;
Class<?> coroutineContinuation = null;
try {
metadata = ClassUtils.forName("kotlin.Metadata", classLoader);
@ -58,6 +61,12 @@ public abstract class KotlinDetector { @@ -58,6 +61,12 @@ public abstract class KotlinDetector {
catch (ClassNotFoundException ex) {
// JVM inline support not available
}
try {
serializable = ClassUtils.forName("kotlinx.serialization.Serializable", classLoader);
}
catch (ClassNotFoundException ex) {
// Kotlin Serialization not available
}
try {
coroutineContinuation = ClassUtils.forName("kotlin.coroutines.Continuation", classLoader);
}
@ -72,6 +81,7 @@ public abstract class KotlinDetector { @@ -72,6 +81,7 @@ public abstract class KotlinDetector {
KOTLIN_PRESENT = (KOTLIN_METADATA != null);
KOTLIN_REFLECT_PRESENT = ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
KOTLIN_JVM_INLINE = (Class<? extends Annotation>) jvmInline;
KOTLIN_SERIALIZABLE = (Class<? extends Annotation>) serializable;
KOTLIN_COROUTINE_CONTINUATION = coroutineContinuation;
}
@ -125,4 +135,26 @@ public abstract class KotlinDetector { @@ -125,4 +135,26 @@ public abstract class KotlinDetector {
return (KOTLIN_JVM_INLINE != null && clazz.getDeclaredAnnotation(KOTLIN_JVM_INLINE) != null);
}
/**
* Determine whether the given {@code ResolvableType} is annotated with {@code @kotlinx.serialization.Serializable}
* at type or generics level.
* @since 7.0
*/
public static boolean hasSerializableAnnotation(ResolvableType type) {
Class<?> resolvedClass = type.resolve();
if (KOTLIN_SERIALIZABLE == null || resolvedClass == null) {
return false;
}
if (resolvedClass.isAnnotationPresent(KOTLIN_SERIALIZABLE)) {
return true;
}
@Nullable Class<?>[] resolvedGenerics = type.resolveGenerics();
for (Class<?> resolvedGeneric : resolvedGenerics) {
if (resolvedGeneric != null && resolvedGeneric.isAnnotationPresent(KOTLIN_SERIALIZABLE)) {
return true;
}
}
return false;
}
}

20
spring-core/src/test/kotlin/org/springframework/core/KotlinDetectorTests.kt

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package org.springframework.core
import kotlinx.serialization.Serializable
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
@ -46,7 +47,26 @@ class KotlinDetectorTests { @@ -46,7 +47,26 @@ class KotlinDetectorTests {
Assertions.assertThat(KotlinDetector.isInlineClass(KotlinDetectorTests::class.java)).isFalse()
}
@Test
fun hasSerializableAnnotation() {
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClass(WithoutSerializable::class.java))).isFalse()
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClass(WithSerializable::class.java))).isTrue()
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(List::class.java, WithoutSerializable::class.java))).isFalse()
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(List::class.java, WithSerializable::class.java))).isTrue()
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(Map::class.java, String::class.java, WithoutSerializable::class.java))).isFalse()
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(Map::class.java, String::class.java, WithSerializable::class.java))).isTrue()
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.NONE)).isFalse()
}
@JvmInline
value class ValueClass(val value: String)
data class WithoutSerializable(val value: String)
@Serializable
data class WithSerializable(val value: String)
}

Loading…
Cancel
Save