Skip to content

Commit a68d607

Browse files
committed
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
1 parent 2459009 commit a68d607

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public abstract class KotlinDetector {
3838

3939
private static final @Nullable Class<? extends Annotation> KOTLIN_JVM_INLINE;
4040

41+
private static final @Nullable Class<? extends Annotation> KOTLIN_SERIALIZABLE;
42+
4143
private static final @Nullable Class<?> KOTLIN_COROUTINE_CONTINUATION;
4244

4345
// For ConstantFieldFeature compliance, otherwise could be deduced from kotlinMetadata
@@ -49,6 +51,7 @@ public abstract class KotlinDetector {
4951
ClassLoader classLoader = KotlinDetector.class.getClassLoader();
5052
Class<?> metadata = null;
5153
Class<?> jvmInline = null;
54+
Class<?> serializable = null;
5255
Class<?> coroutineContinuation = null;
5356
try {
5457
metadata = ClassUtils.forName("kotlin.Metadata", classLoader);
@@ -58,6 +61,12 @@ public abstract class KotlinDetector {
5861
catch (ClassNotFoundException ex) {
5962
// JVM inline support not available
6063
}
64+
try {
65+
serializable = ClassUtils.forName("kotlinx.serialization.Serializable", classLoader);
66+
}
67+
catch (ClassNotFoundException ex) {
68+
// Kotlin Serialization not available
69+
}
6170
try {
6271
coroutineContinuation = ClassUtils.forName("kotlin.coroutines.Continuation", classLoader);
6372
}
@@ -72,6 +81,7 @@ public abstract class KotlinDetector {
7281
KOTLIN_PRESENT = (KOTLIN_METADATA != null);
7382
KOTLIN_REFLECT_PRESENT = ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
7483
KOTLIN_JVM_INLINE = (Class<? extends Annotation>) jvmInline;
84+
KOTLIN_SERIALIZABLE = (Class<? extends Annotation>) serializable;
7585
KOTLIN_COROUTINE_CONTINUATION = coroutineContinuation;
7686
}
7787

@@ -125,4 +135,26 @@ public static boolean isInlineClass(Class<?> clazz) {
125135
return (KOTLIN_JVM_INLINE != null && clazz.getDeclaredAnnotation(KOTLIN_JVM_INLINE) != null);
126136
}
127137

138+
/**
139+
* Determine whether the given {@code ResolvableType} is annotated with {@code @kotlinx.serialization.Serializable}
140+
* at type or generics level.
141+
* @since 7.0
142+
*/
143+
public static boolean hasSerializableAnnotation(ResolvableType type) {
144+
Class<?> resolvedClass = type.resolve();
145+
if (KOTLIN_SERIALIZABLE == null || resolvedClass == null) {
146+
return false;
147+
}
148+
if (resolvedClass.isAnnotationPresent(KOTLIN_SERIALIZABLE)) {
149+
return true;
150+
}
151+
@Nullable Class<?>[] resolvedGenerics = type.resolveGenerics();
152+
for (Class<?> resolvedGeneric : resolvedGenerics) {
153+
if (resolvedGeneric != null && resolvedGeneric.isAnnotationPresent(KOTLIN_SERIALIZABLE)) {
154+
return true;
155+
}
156+
}
157+
return false;
158+
}
159+
128160
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.core
1717

18+
import kotlinx.serialization.Serializable
1819
import org.assertj.core.api.Assertions
1920
import org.junit.jupiter.api.Test
2021

@@ -46,7 +47,26 @@ class KotlinDetectorTests {
4647
Assertions.assertThat(KotlinDetector.isInlineClass(KotlinDetectorTests::class.java)).isFalse()
4748
}
4849

50+
@Test
51+
fun hasSerializableAnnotation() {
52+
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClass(WithoutSerializable::class.java))).isFalse()
53+
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClass(WithSerializable::class.java))).isTrue()
54+
55+
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(List::class.java, WithoutSerializable::class.java))).isFalse()
56+
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(List::class.java, WithSerializable::class.java))).isTrue()
57+
58+
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(Map::class.java, String::class.java, WithoutSerializable::class.java))).isFalse()
59+
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.forClassWithGenerics(Map::class.java, String::class.java, WithSerializable::class.java))).isTrue()
60+
61+
Assertions.assertThat(KotlinDetector.hasSerializableAnnotation(ResolvableType.NONE)).isFalse()
62+
}
63+
4964
@JvmInline
5065
value class ValueClass(val value: String)
5166

67+
data class WithoutSerializable(val value: String)
68+
69+
@Serializable
70+
data class WithSerializable(val value: String)
71+
5272
}

0 commit comments

Comments
 (0)