Skip to content

Commit ab64305

Browse files
committed
Efficient Kotlin metadata detection
Follow-up of 3991ab4. Issue: SPR-15673
1 parent cea9d1d commit ab64305

File tree

3 files changed

+82
-53
lines changed

3 files changed

+82
-53
lines changed

spring-beans/src/main/java/org/springframework/beans/BeanUtils.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,18 @@ public abstract class BeanUtils {
7171
Collections.newSetFromMap(new ConcurrentReferenceHashMap<>(64));
7272

7373
@Nullable
74-
private static Class<?> kotlinMetadata;
74+
private static final Class<?> kotlinMetadata;
7575

7676
static {
77+
Class<?> metadata;
7778
try {
78-
kotlinMetadata = ClassUtils.forName("kotlin.Metadata", BeanUtils.class.getClassLoader());
79+
metadata = ClassUtils.forName("kotlin.Metadata", BeanUtils.class.getClassLoader());
7980
}
8081
catch (ClassNotFoundException ex) {
8182
// Kotlin API not available - no special support for Kotlin class instantiation
82-
kotlinMetadata = null;
83+
metadata = null;
8384
}
85+
kotlinMetadata = metadata;
8486
}
8587

8688

@@ -125,7 +127,7 @@ public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationExc
125127
throw new BeanInstantiationException(clazz, "Specified class is an interface");
126128
}
127129
try {
128-
Constructor<T> ctor = (isKotlinClass(clazz) ?
130+
Constructor<T> ctor = (useKotlinSupport(clazz) ?
129131
KotlinDelegate.findPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
130132
if (ctor == null) {
131133
throw new BeanInstantiationException(clazz, "No default constructor found");
@@ -172,7 +174,7 @@ public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws
172174
Assert.notNull(ctor, "Constructor must not be null");
173175
try {
174176
ReflectionUtils.makeAccessible(ctor);
175-
return (isKotlinClass(ctor.getDeclaringClass()) ?
177+
return (useKotlinSupport(ctor.getDeclaringClass()) ?
176178
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
177179
}
178180
catch (InstantiationException ex) {
@@ -340,7 +342,7 @@ else if (!method.isBridge() && targetMethod.getParameterCount() == numParams) {
340342
@Nullable
341343
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
342344
Assert.notNull(clazz, "Class must not be null");
343-
if (isKotlinClass(clazz)) {
345+
if (useKotlinSupport(clazz)) {
344346
return KotlinDelegate.findPrimaryConstructor(clazz);
345347
}
346348
else {
@@ -707,10 +709,10 @@ private static void copyProperties(Object source, Object target, @Nullable Class
707709
}
708710

709711
/**
710-
* Return true if the specified class is a Kotlin one.
712+
* Return true if Kotlin is present and if the specified class is a Kotlin one.
711713
*/
712714
@SuppressWarnings("unchecked")
713-
private static boolean isKotlinClass(Class<?> clazz) {
715+
private static boolean useKotlinSupport(Class<?> clazz) {
714716
return (kotlinMetadata != null &&
715717
clazz.getDeclaredAnnotation((Class<? extends Annotation>) kotlinMetadata) != null);
716718
}

spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.util.Map;
2727
import java.util.Optional;
2828

29-
import kotlin.Metadata;
3029
import kotlin.reflect.KProperty;
3130
import kotlin.reflect.jvm.ReflectJvmMapping;
3231

@@ -52,8 +51,20 @@
5251
@SuppressWarnings("serial")
5352
public class DependencyDescriptor extends InjectionPoint implements Serializable {
5453

55-
private static final boolean kotlinPresent =
56-
ClassUtils.isPresent("kotlin.Metadata", DependencyDescriptor.class.getClassLoader());
54+
@Nullable
55+
private static final Class<?> kotlinMetadata;
56+
57+
static {
58+
Class<?> metadata;
59+
try {
60+
metadata = ClassUtils.forName("kotlin.Metadata", DependencyDescriptor.class.getClassLoader());
61+
}
62+
catch (ClassNotFoundException ex) {
63+
// Kotlin API not available - no Kotlin support
64+
metadata = null;
65+
}
66+
kotlinMetadata = metadata;
67+
}
5768

5869

5970
private final Class<?> declaringClass;
@@ -172,13 +183,22 @@ public boolean isRequired() {
172183

173184
if (this.field != null) {
174185
return !(this.field.getType() == Optional.class || hasNullableAnnotation() ||
175-
(kotlinPresent && KotlinDelegate.isNullable(this.field)));
186+
(useKotlinSupport(this.field.getDeclaringClass()) && KotlinDelegate.isNullable(this.field)));
176187
}
177188
else {
178189
return !obtainMethodParameter().isOptional();
179190
}
180191
}
181192

193+
/**
194+
* Return true if Kotlin is present and if the specified class is a Kotlin one.
195+
*/
196+
@SuppressWarnings("unchecked")
197+
private static boolean useKotlinSupport(Class<?> clazz) {
198+
return (kotlinMetadata != null &&
199+
clazz.getDeclaredAnnotation((Class<? extends Annotation>) kotlinMetadata) != null);
200+
}
201+
182202
/**
183203
* Check whether the underlying field is annotated with any variant of a
184204
* {@code Nullable} annotation, e.g. {@code javax.annotation.Nullable} or
@@ -435,11 +455,8 @@ private static class KotlinDelegate {
435455
* Check whether the specified {@link Field} represents a nullable Kotlin type or not.
436456
*/
437457
public static boolean isNullable(Field field) {
438-
if (field.getDeclaringClass().isAnnotationPresent(Metadata.class)) {
439-
KProperty<?> property = ReflectJvmMapping.getKotlinProperty(field);
440-
return (property != null && property.getReturnType().isMarkedNullable());
441-
}
442-
return false;
458+
KProperty<?> property = ReflectJvmMapping.getKotlinProperty(field);
459+
return (property != null && property.getReturnType().isMarkedNullable());
443460
}
444461
}
445462

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

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,20 @@
5858
*/
5959
public class MethodParameter {
6060

61-
private static final boolean kotlinPresent =
62-
ClassUtils.isPresent("kotlin.Metadata", MethodParameter.class.getClassLoader());
61+
@Nullable
62+
private static final Class<?> kotlinMetadata;
63+
64+
static {
65+
Class<?> metadata;
66+
try {
67+
metadata = ClassUtils.forName("kotlin.Metadata", MethodParameter.class.getClassLoader());
68+
}
69+
catch (ClassNotFoundException ex) {
70+
// Kotlin API not available - no Kotlin support
71+
metadata = null;
72+
}
73+
kotlinMetadata = metadata;
74+
}
6375

6476

6577
private final Executable executable;
@@ -341,7 +353,16 @@ public MethodParameter nested() {
341353
*/
342354
public boolean isOptional() {
343355
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
344-
(kotlinPresent && KotlinDelegate.isNullable(this)));
356+
(useKotlinSupport(this.getContainingClass()) && KotlinDelegate.isNullable(this)));
357+
}
358+
359+
/**
360+
* Return true if Kotlin is present and if the specified class is a Kotlin one.
361+
*/
362+
@SuppressWarnings("unchecked")
363+
private static boolean useKotlinSupport(Class<?> clazz) {
364+
return (kotlinMetadata != null &&
365+
clazz.getDeclaredAnnotation((Class<? extends Annotation>) kotlinMetadata) != null);
345366
}
346367

347368
/**
@@ -736,41 +757,30 @@ private static class KotlinDelegate {
736757
* Check whether the specified {@link MethodParameter} represents a nullable Kotlin type or not.
737758
*/
738759
public static boolean isNullable(MethodParameter param) {
739-
if (isKotlinClass(param.getContainingClass())) {
740-
Method method = param.getMethod();
741-
Constructor<?> ctor = param.getConstructor();
742-
int index = param.getParameterIndex();
743-
if (method != null && index == -1) {
744-
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
745-
return (function != null && function.getReturnType().isMarkedNullable());
760+
Method method = param.getMethod();
761+
Constructor<?> ctor = param.getConstructor();
762+
int index = param.getParameterIndex();
763+
if (method != null && index == -1) {
764+
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
765+
return (function != null && function.getReturnType().isMarkedNullable());
766+
}
767+
else {
768+
KFunction<?> function = null;
769+
if (method != null) {
770+
function = ReflectJvmMapping.getKotlinFunction(method);
746771
}
747-
else {
748-
KFunction<?> function = null;
749-
if (method != null) {
750-
function = ReflectJvmMapping.getKotlinFunction(method);
751-
}
752-
else if (ctor != null) {
753-
function = ReflectJvmMapping.getKotlinFunction(ctor);
754-
}
755-
if (function != null) {
756-
List<KParameter> parameters = function.getParameters();
757-
return parameters
758-
.stream()
759-
.filter(p -> KParameter.Kind.VALUE.equals(p.getKind()))
760-
.collect(Collectors.toList())
761-
.get(index)
762-
.getType()
763-
.isMarkedNullable();
764-
}
772+
else if (ctor != null) {
773+
function = ReflectJvmMapping.getKotlinFunction(ctor);
765774
}
766-
}
767-
return false;
768-
}
769-
770-
private static boolean isKotlinClass(Class<?> clazz) {
771-
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
772-
if (annotation.annotationType().getName().equals("kotlin.Metadata")) {
773-
return true;
775+
if (function != null) {
776+
List<KParameter> parameters = function.getParameters();
777+
return parameters
778+
.stream()
779+
.filter(p -> KParameter.Kind.VALUE.equals(p.getKind()))
780+
.collect(Collectors.toList())
781+
.get(index)
782+
.getType()
783+
.isMarkedNullable();
774784
}
775785
}
776786
return false;

0 commit comments

Comments
 (0)