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