Refine KotlinDetector usages and implementation
This commit refines KotlinDetector usages and implementation in order to remove preliminary KotlinDetector#isKotlinReflectPresent invocations and to ensure that KotlinDetector methods are implemented safely and efficiently for such use case. Closes gh-34275
This commit is contained in:
parent
ffd7b93dde
commit
1763334180
|
@ -186,7 +186,7 @@ public abstract class BeanUtils {
|
|||
Assert.notNull(ctor, "Constructor must not be null");
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(ctor);
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
|
||||
if (KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
|
||||
return KotlinDelegate.instantiateClass(ctor, args);
|
||||
}
|
||||
else {
|
||||
|
@ -279,7 +279,7 @@ public abstract class BeanUtils {
|
|||
*/
|
||||
public static <T> @Nullable Constructor<T> findPrimaryConstructor(Class<T> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(clazz)) {
|
||||
if (KotlinDetector.isKotlinType(clazz)) {
|
||||
return KotlinDelegate.findPrimaryConstructor(clazz);
|
||||
}
|
||||
if (clazz.isRecord()) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -160,7 +160,7 @@ public class InstanceSupplierCodeGenerator {
|
|||
registeredBean.getBeanName(), constructor, registeredBean.getBeanClass());
|
||||
|
||||
Class<?> publicType = descriptor.publicType();
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDelegate.hasConstructorWithOptionalParameter(publicType)) {
|
||||
if (KotlinDetector.isKotlinType(publicType) && KotlinDelegate.hasConstructorWithOptionalParameter(publicType)) {
|
||||
return generateCodeForInaccessibleConstructor(descriptor,
|
||||
hints -> hints.registerType(publicType, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
}
|
||||
|
@ -408,7 +408,6 @@ public class InstanceSupplierCodeGenerator {
|
|||
private static class KotlinDelegate {
|
||||
|
||||
public static boolean hasConstructorWithOptionalParameter(Class<?> beanClass) {
|
||||
if (KotlinDetector.isKotlinType(beanClass)) {
|
||||
KClass<?> kClass = JvmClassMappingKt.getKotlinClass(beanClass);
|
||||
for (KFunction<?> constructor : kClass.getConstructors()) {
|
||||
for (KParameter parameter : constructor.getParameters()) {
|
||||
|
@ -417,7 +416,6 @@ public class InstanceSupplierCodeGenerator {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -163,9 +163,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
|||
|
||||
if (this.field != null) {
|
||||
return !(this.field.getType() == Optional.class || hasNullableAnnotation() ||
|
||||
(KotlinDetector.isKotlinReflectPresent() &&
|
||||
KotlinDetector.isKotlinType(this.field.getDeclaringClass()) &&
|
||||
KotlinDelegate.isNullable(this.field)));
|
||||
(KotlinDetector.isKotlinType(this.field.getDeclaringClass()) && KotlinDelegate.isNullable(this.field)));
|
||||
}
|
||||
else {
|
||||
return !obtainMethodParameter().isOptional();
|
||||
|
|
|
@ -624,7 +624,7 @@ class ConstructorResolver {
|
|||
"Invalid factory method '" + mbd.getFactoryMethodName() + "' on class [" +
|
||||
factoryClass.getName() + "]: needs to have a non-void return type!");
|
||||
}
|
||||
else if (KotlinDetector.isKotlinPresent() && KotlinDetector.isSuspendingFunction(factoryMethodToUse)) {
|
||||
else if (KotlinDetector.isSuspendingFunction(factoryMethodToUse)) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Invalid factory method '" + mbd.getFactoryMethodName() + "' on class [" +
|
||||
factoryClass.getName() + "]: suspending functions are not supported!");
|
||||
|
|
|
@ -1092,7 +1092,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
() -> Mono.from(adapter.toPublisher(invokeOperation(invoker))).toFuture())));
|
||||
}
|
||||
}
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isSuspendingFunction(method)) {
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
return Mono.fromFuture(cache.retrieve(key, () -> {
|
||||
Mono<?> mono = ((Mono<?>) invokeOperation(invoker));
|
||||
if (mono == null) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -81,7 +81,7 @@ abstract class ScheduledAnnotationReactiveSupport {
|
|||
* Kotlin coroutines bridge are not present at runtime
|
||||
*/
|
||||
public static boolean isReactive(Method method) {
|
||||
if (KotlinDetector.isKotlinPresent() && KotlinDetector.isSuspendingFunction(method)) {
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
// Note that suspending functions declared without args have a single Continuation
|
||||
// parameter in reflective inspection
|
||||
Assert.isTrue(method.getParameterCount() == 1,
|
||||
|
@ -138,7 +138,7 @@ abstract class ScheduledAnnotationReactiveSupport {
|
|||
* to a {@code Flux} with a checkpoint String, allowing for better debugging.
|
||||
*/
|
||||
static Publisher<?> getPublisherFor(Method method, Object bean) {
|
||||
if (KotlinDetector.isKotlinPresent() && KotlinDetector.isSuspendingFunction(method)) {
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
return CoroutinesUtils.invokeSuspendingFunction(method, bean, (Object[]) method.getParameters());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -24,7 +24,8 @@ import org.jspecify.annotations.Nullable;
|
|||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A common delegate for detecting Kotlin's presence and for identifying Kotlin types.
|
||||
* A common delegate for detecting Kotlin's presence and for identifying Kotlin types. All the methods of this class
|
||||
* can be safely used without any preliminary classpath checks.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
|
@ -37,6 +38,8 @@ public abstract class KotlinDetector {
|
|||
|
||||
private static final @Nullable Class<? extends Annotation> kotlinJvmInline;
|
||||
|
||||
private static final @Nullable Class<?> kotlinCoroutineContinuation;
|
||||
|
||||
// For ConstantFieldFeature compliance, otherwise could be deduced from kotlinMetadata
|
||||
private static final boolean kotlinPresent;
|
||||
|
||||
|
@ -46,6 +49,7 @@ public abstract class KotlinDetector {
|
|||
ClassLoader classLoader = KotlinDetector.class.getClassLoader();
|
||||
Class<?> metadata = null;
|
||||
Class<?> jvmInline = null;
|
||||
Class<?> coroutineContinuation = null;
|
||||
try {
|
||||
metadata = ClassUtils.forName("kotlin.Metadata", classLoader);
|
||||
try {
|
||||
|
@ -54,14 +58,21 @@ public abstract class KotlinDetector {
|
|||
catch (ClassNotFoundException ex) {
|
||||
// JVM inline support not available
|
||||
}
|
||||
try {
|
||||
coroutineContinuation = ClassUtils.forName("kotlin.coroutines.Continuation", classLoader);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Coroutines support not available
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Kotlin API not available - no Kotlin support
|
||||
}
|
||||
kotlinMetadata = (Class<? extends Annotation>) metadata;
|
||||
kotlinPresent = (kotlinMetadata != null);
|
||||
kotlinReflectPresent = kotlinPresent && ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
|
||||
kotlinReflectPresent = ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
|
||||
kotlinJvmInline = (Class<? extends Annotation>) jvmInline;
|
||||
kotlinCoroutineContinuation = coroutineContinuation;
|
||||
}
|
||||
|
||||
|
||||
|
@ -89,7 +100,7 @@ public abstract class KotlinDetector {
|
|||
* as invokedynamic has become the default method for lambda generation.
|
||||
*/
|
||||
public static boolean isKotlinType(Class<?> clazz) {
|
||||
return (kotlinMetadata != null && clazz.getDeclaredAnnotation(kotlinMetadata) != null);
|
||||
return (kotlinPresent && clazz.getDeclaredAnnotation(kotlinMetadata) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,14 +108,12 @@ public abstract class KotlinDetector {
|
|||
* @since 5.3
|
||||
*/
|
||||
public static boolean isSuspendingFunction(Method method) {
|
||||
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
|
||||
Class<?>[] types = method.getParameterTypes();
|
||||
if (types.length > 0 && "kotlin.coroutines.Continuation".equals(types[types.length - 1].getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (kotlinCoroutineContinuation == null) {
|
||||
return false;
|
||||
}
|
||||
int parameterCount = method.getParameterCount();
|
||||
return (parameterCount > 0 && method.getParameterTypes()[parameterCount - 1] == kotlinCoroutineContinuation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given {@code Class} is an inline class
|
||||
|
|
|
@ -396,9 +396,7 @@ public class MethodParameter {
|
|||
*/
|
||||
public boolean isOptional() {
|
||||
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
|
||||
(KotlinDetector.isKotlinReflectPresent() &&
|
||||
KotlinDetector.isKotlinType(getContainingClass()) &&
|
||||
KotlinDelegate.isOptional(this)));
|
||||
(KotlinDetector.isKotlinType(getContainingClass()) && KotlinDelegate.isOptional(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -508,7 +506,7 @@ public class MethodParameter {
|
|||
if (this.parameterIndex < 0) {
|
||||
Method method = getMethod();
|
||||
paramType = (method != null ?
|
||||
(KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass()) ?
|
||||
(KotlinDetector.isKotlinType(getContainingClass()) ?
|
||||
KotlinDelegate.getGenericReturnType(method) : method.getGenericReturnType()) : void.class);
|
||||
}
|
||||
else {
|
||||
|
@ -536,7 +534,7 @@ public class MethodParameter {
|
|||
if (method == null) {
|
||||
return void.class;
|
||||
}
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(getContainingClass())) {
|
||||
if (KotlinDetector.isKotlinType(getContainingClass())) {
|
||||
return KotlinDelegate.getReturnType(method);
|
||||
}
|
||||
return method.getReturnType();
|
||||
|
|
|
@ -374,7 +374,7 @@ public class SpringFactoriesLoader {
|
|||
|
||||
T instantiate(@Nullable ArgumentResolver argumentResolver) throws Exception {
|
||||
Object[] args = resolveArgs(argumentResolver);
|
||||
if (isKotlinType(this.constructor.getDeclaringClass())) {
|
||||
if (KotlinDetector.isKotlinType(this.constructor.getDeclaringClass())) {
|
||||
return KotlinDelegate.instantiate(this.constructor, args);
|
||||
}
|
||||
return this.constructor.newInstance(args);
|
||||
|
@ -408,14 +408,10 @@ public class SpringFactoriesLoader {
|
|||
}
|
||||
|
||||
private static @Nullable Constructor<?> findPrimaryKotlinConstructor(Class<?> factoryImplementationClass) {
|
||||
return (isKotlinType(factoryImplementationClass) ?
|
||||
return (KotlinDetector.isKotlinType(factoryImplementationClass) ?
|
||||
KotlinDelegate.findPrimaryConstructor(factoryImplementationClass) : null);
|
||||
}
|
||||
|
||||
private static boolean isKotlinType(Class<?> factoryImplementationClass) {
|
||||
return KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(factoryImplementationClass);
|
||||
}
|
||||
|
||||
private static @Nullable Constructor<?> findSingleConstructor(Constructor<?>[] constructors) {
|
||||
return (constructors.length == 1 ? constructors[0] : null);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -558,8 +558,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
|||
|
||||
private static boolean isKotlinProperty(Method method, String methodSuffix) {
|
||||
Class<?> clazz = method.getDeclaringClass();
|
||||
return KotlinDetector.isKotlinReflectPresent() &&
|
||||
KotlinDetector.isKotlinType(clazz) &&
|
||||
return KotlinDetector.isKotlinType(clazz) &&
|
||||
KotlinDelegate.isKotlinProperty(method, methodSuffix);
|
||||
}
|
||||
|
||||
|
|
|
@ -868,8 +868,7 @@ public class Jackson2ObjectMapperBuilder {
|
|||
// jackson-datatype-jsr310 not available
|
||||
}
|
||||
|
||||
// Kotlin present?
|
||||
if (KotlinDetector.isKotlinPresent()) {
|
||||
if (KotlinDetector.isKotlinReflectPresent()) {
|
||||
try {
|
||||
Class<? extends Module> kotlinModuleClass = (Class<? extends Module>)
|
||||
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -103,8 +103,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
|||
|
||||
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
||||
MethodParameter nestedParameter = parameter.nestedIfOptional();
|
||||
boolean hasDefaultValue = KotlinDetector.isKotlinReflectPresent() &&
|
||||
KotlinDetector.isKotlinType(parameter.getDeclaringClass()) &&
|
||||
boolean hasDefaultValue = KotlinDetector.isKotlinType(parameter.getDeclaringClass()) &&
|
||||
KotlinDelegate.hasDefaultValue(nestedParameter);
|
||||
|
||||
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
|
||||
|
@ -276,7 +275,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
|||
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
if (KotlinDetector.isKotlinPresent() && KotlinDetector.isInlineClass(parameterType)) {
|
||||
if (KotlinDetector.isInlineClass(parameterType)) {
|
||||
Constructor<?> ctor = BeanUtils.findPrimaryConstructor(parameterType);
|
||||
if (ctor != null) {
|
||||
parameterType = ctor.getParameterTypes()[0];
|
||||
|
|
|
@ -242,14 +242,12 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
|||
protected @Nullable Object doInvoke(@Nullable Object... args) throws Exception {
|
||||
Method method = getBridgedMethod();
|
||||
try {
|
||||
if (KotlinDetector.isKotlinReflectPresent()) {
|
||||
if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
|
||||
if (KotlinDetector.isSuspendingFunction(method)) {
|
||||
return invokeSuspendingFunction(method, getBean(), args);
|
||||
}
|
||||
else if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
|
||||
return KotlinDelegate.invokeFunction(method, getBean(), args);
|
||||
}
|
||||
}
|
||||
return method.invoke(getBean(), args);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -191,12 +191,9 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
|||
Method method = getBridgedMethod();
|
||||
boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
|
||||
try {
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(method.getDeclaringClass())) {
|
||||
value = KotlinDelegate.invokeFunction(method, getBean(), args, isSuspendingFunction, exchange);
|
||||
}
|
||||
else {
|
||||
value = method.invoke(getBean(), args);
|
||||
}
|
||||
value = (KotlinDetector.isKotlinType(method.getDeclaringClass()) ?
|
||||
KotlinDelegate.invokeFunction(method, getBean(), args, isSuspendingFunction, exchange) :
|
||||
method.invoke(getBean(), args));
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
assertTargetBean(getBridgedMethod(), getBean(), args);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -195,7 +195,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
|||
|
||||
WebDataBinder binder = bindingContext.createDataBinder(exchange, namedValueInfo.name);
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
if (KotlinDetector.isKotlinPresent() && KotlinDetector.isInlineClass(parameterType)) {
|
||||
if (KotlinDetector.isInlineClass(parameterType)) {
|
||||
Constructor<?> ctor = BeanUtils.findPrimaryConstructor(parameterType);
|
||||
if (ctor != null) {
|
||||
parameterType = ctor.getParameterTypes()[0];
|
||||
|
@ -222,8 +222,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
|||
|
||||
return Mono.fromSupplier(() -> {
|
||||
Object value = null;
|
||||
boolean hasDefaultValue = KotlinDetector.isKotlinReflectPresent() &&
|
||||
KotlinDetector.isKotlinType(parameter.getDeclaringClass()) &&
|
||||
boolean hasDefaultValue = KotlinDetector.isKotlinType(parameter.getDeclaringClass()) &&
|
||||
KotlinDelegate.hasDefaultValue(parameter);
|
||||
if (namedValueInfo.defaultValue != null) {
|
||||
value = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
|
||||
|
|
Loading…
Reference in New Issue