diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java index 2abd061bac..5b7cccba95 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java @@ -17,8 +17,8 @@ package org.springframework.core.convert.support; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; @@ -59,9 +59,9 @@ import org.springframework.util.ReflectionUtils; * * *

Warning: this converter does not support the - * {@link Object#toString()} method for converting from a {@code sourceType} - * to {@code java.lang.String}. For {@code toString()} support, use - * {@link FallbackObjectToStringConverter} instead. + * {@link Object#toString()} or {@link String#valueOf(Object)} methods for converting + * from a {@code sourceType} to {@code java.lang.String}. For {@code toString()} + * support, use {@link FallbackObjectToStringConverter} instead. * * @author Keith Donald * @author Juergen Hoeller @@ -71,8 +71,9 @@ import org.springframework.util.ReflectionUtils; */ final class ObjectToObjectConverter implements ConditionalGenericConverter { - // Cache for the latest to-method resolved on a given Class - private static final Map, Member> conversionMemberCache = + // Cache for the latest to-method, static factory method, or factory constructor + // resolved on a given Class + private static final Map, Executable> conversionExecutableCache = new ConcurrentReferenceHashMap<>(32); @@ -95,10 +96,10 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter { } Class sourceClass = sourceType.getType(); Class targetClass = targetType.getType(); - Member member = getValidatedMember(targetClass, sourceClass); + Executable executable = getValidatedExecutable(targetClass, sourceClass); try { - if (member instanceof Method method) { + if (executable instanceof Method method) { ReflectionUtils.makeAccessible(method); if (!Modifier.isStatic(method.getModifiers())) { return method.invoke(source); @@ -107,7 +108,7 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter { return method.invoke(null, source); } } - else if (member instanceof Constructor constructor) { + else if (executable instanceof Constructor constructor) { ReflectionUtils.makeAccessible(constructor); return constructor.newInstance(source); } @@ -128,40 +129,39 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter { } - static boolean hasConversionMethodOrConstructor(Class targetClass, Class sourceClass) { - return (getValidatedMember(targetClass, sourceClass) != null); + return (getValidatedExecutable(targetClass, sourceClass) != null); } @Nullable - private static Member getValidatedMember(Class targetClass, Class sourceClass) { - Member member = conversionMemberCache.get(targetClass); - if (isApplicable(member, sourceClass)) { - return member; + private static Executable getValidatedExecutable(Class targetClass, Class sourceClass) { + Executable executable = conversionExecutableCache.get(targetClass); + if (isApplicable(executable, sourceClass)) { + return executable; } - member = determineToMethod(targetClass, sourceClass); - if (member == null) { - member = determineFactoryMethod(targetClass, sourceClass); - if (member == null) { - member = determineFactoryConstructor(targetClass, sourceClass); - if (member == null) { + executable = determineToMethod(targetClass, sourceClass); + if (executable == null) { + executable = determineFactoryMethod(targetClass, sourceClass); + if (executable == null) { + executable = determineFactoryConstructor(targetClass, sourceClass); + if (executable == null) { return null; } } } - conversionMemberCache.put(targetClass, member); - return member; + conversionExecutableCache.put(targetClass, executable); + return executable; } - private static boolean isApplicable(Member member, Class sourceClass) { - if (member instanceof Method method) { + private static boolean isApplicable(Executable executable, Class sourceClass) { + if (executable instanceof Method method) { return (!Modifier.isStatic(method.getModifiers()) ? ClassUtils.isAssignable(method.getDeclaringClass(), sourceClass) : method.getParameterTypes()[0] == sourceClass); } - else if (member instanceof Constructor constructor) { + else if (executable instanceof Constructor constructor) { return (constructor.getParameterTypes()[0] == sourceClass); } else {