Skip to content

Commit 09aa59f

Browse files
committed
Avoid ResolvableType for simple assignability check in copyProperties
Closes gh-27246
1 parent 7874a59 commit 09aa59f

File tree

1 file changed

+29
-21
lines changed

1 file changed

+29
-21
lines changed

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

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.lang.reflect.InvocationTargetException;
2424
import java.lang.reflect.Method;
2525
import java.lang.reflect.Modifier;
26+
import java.lang.reflect.Type;
2627
import java.net.URI;
2728
import java.net.URL;
2829
import java.time.temporal.Temporal;
@@ -615,8 +616,8 @@ public static Class<?> findPropertyType(String propertyName, @Nullable Class<?>.
615616
* @return a corresponding MethodParameter object
616617
*/
617618
public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) {
618-
if (pd instanceof GenericTypeAwarePropertyDescriptor typeAwarePd) {
619-
return new MethodParameter(typeAwarePd.getWriteMethodParameter());
619+
if (pd instanceof GenericTypeAwarePropertyDescriptor gpd) {
620+
return new MethodParameter(gpd.getWriteMethodParameter());
620621
}
621622
else {
622623
Method writeMethod = pd.getWriteMethod();
@@ -787,38 +788,28 @@ private static void copyProperties(Object source, Object target, @Nullable Class
787788
if (editable != null) {
788789
if (!editable.isInstance(target)) {
789790
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
790-
"] not assignable to Editable class [" + editable.getName() + "]");
791+
"] not assignable to editable class [" + editable.getName() + "]");
791792
}
792793
actualEditable = editable;
793794
}
794795
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
795796
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
797+
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
798+
CachedIntrospectionResults.forClass(source.getClass()) : null);
796799

797800
for (PropertyDescriptor targetPd : targetPds) {
798801
Method writeMethod = targetPd.getWriteMethod();
799802
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
800-
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
803+
PropertyDescriptor sourcePd = (sourceResults != null ?
804+
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
801805
if (sourcePd != null) {
802806
Method readMethod = sourcePd.getReadMethod();
803807
if (readMethod != null) {
804-
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
805-
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
806-
807-
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
808-
boolean isAssignable =
809-
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
810-
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
811-
targetResolvableType.isAssignableFrom(sourceResolvableType));
812-
813-
if (isAssignable) {
808+
if (isAssignable(writeMethod, readMethod)) {
814809
try {
815-
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
816-
readMethod.setAccessible(true);
817-
}
810+
ReflectionUtils.makeAccessible(readMethod);
818811
Object value = readMethod.invoke(source);
819-
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
820-
writeMethod.setAccessible(true);
821-
}
812+
ReflectionUtils.makeAccessible(writeMethod);
822813
writeMethod.invoke(target, value);
823814
}
824815
catch (Throwable ex) {
@@ -832,6 +823,24 @@ private static void copyProperties(Object source, Object target, @Nullable Class
832823
}
833824
}
834825

826+
private static boolean isAssignable(Method writeMethod, Method readMethod) {
827+
Type paramType = writeMethod.getGenericParameterTypes()[0];
828+
if (paramType instanceof Class<?> clazz) {
829+
return ClassUtils.isAssignable(clazz, readMethod.getReturnType());
830+
}
831+
else if (paramType.equals(readMethod.getGenericReturnType())) {
832+
return true;
833+
}
834+
else {
835+
ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod);
836+
ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0);
837+
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
838+
return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ?
839+
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
840+
targetType.isAssignableFrom(sourceType));
841+
}
842+
}
843+
835844

836845
/**
837846
* Inner class to avoid a hard dependency on Kotlin at runtime.
@@ -896,7 +905,6 @@ public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
896905
}
897906
return kotlinConstructor.callBy(argParameters);
898907
}
899-
900908
}
901909

902910
}

0 commit comments

Comments
 (0)