From 09aa59f9e79e19a2f09e66002c665b6a5a03ae20 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 24 Oct 2023 22:53:00 +0200 Subject: [PATCH] Avoid ResolvableType for simple assignability check in copyProperties Closes gh-27246 --- .../org/springframework/beans/BeanUtils.java | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index 62676e04cd0..3df583a48a6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -23,6 +23,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.net.URI; import java.net.URL; import java.time.temporal.Temporal; @@ -615,8 +616,8 @@ public abstract class BeanUtils { * @return a corresponding MethodParameter object */ public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) { - if (pd instanceof GenericTypeAwarePropertyDescriptor typeAwarePd) { - return new MethodParameter(typeAwarePd.getWriteMethodParameter()); + if (pd instanceof GenericTypeAwarePropertyDescriptor gpd) { + return new MethodParameter(gpd.getWriteMethodParameter()); } else { Method writeMethod = pd.getWriteMethod(); @@ -787,38 +788,28 @@ public abstract class BeanUtils { if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + - "] not assignable to Editable class [" + editable.getName() + "]"); + "] not assignable to editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); Set ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null); + CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ? + CachedIntrospectionResults.forClass(source.getClass()) : null); for (PropertyDescriptor targetPd : targetPds) { Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) { - PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); + PropertyDescriptor sourcePd = (sourceResults != null ? + sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); if (readMethod != null) { - ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod); - ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0); - - // Ignore generic types in assignable check if either ResolvableType has unresolvable generics. - boolean isAssignable = - (sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ? - ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) : - targetResolvableType.isAssignableFrom(sourceResolvableType)); - - if (isAssignable) { + if (isAssignable(writeMethod, readMethod)) { try { - if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { - readMethod.setAccessible(true); - } + ReflectionUtils.makeAccessible(readMethod); Object value = readMethod.invoke(source); - if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { - writeMethod.setAccessible(true); - } + ReflectionUtils.makeAccessible(writeMethod); writeMethod.invoke(target, value); } catch (Throwable ex) { @@ -832,6 +823,24 @@ public abstract class BeanUtils { } } + private static boolean isAssignable(Method writeMethod, Method readMethod) { + Type paramType = writeMethod.getGenericParameterTypes()[0]; + if (paramType instanceof Class clazz) { + return ClassUtils.isAssignable(clazz, readMethod.getReturnType()); + } + else if (paramType.equals(readMethod.getGenericReturnType())) { + return true; + } + else { + ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod); + ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0); + // Ignore generic types in assignable check if either ResolvableType has unresolvable generics. + return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ? + ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) : + targetType.isAssignableFrom(sourceType)); + } + } + /** * Inner class to avoid a hard dependency on Kotlin at runtime. @@ -896,7 +905,6 @@ public abstract class BeanUtils { } return kotlinConstructor.callBy(argParameters); } - } }