Avoid ResolvableType for simple assignability check in copyProperties
Closes gh-27246
This commit is contained in:
		
							parent
							
								
									7874a59771
								
							
						
					
					
						commit
						09aa59f9e7
					
				|  | @ -23,6 +23,7 @@ import java.lang.reflect.Constructor; | ||||||
| import java.lang.reflect.InvocationTargetException; | import java.lang.reflect.InvocationTargetException; | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
| import java.lang.reflect.Modifier; | import java.lang.reflect.Modifier; | ||||||
|  | import java.lang.reflect.Type; | ||||||
| import java.net.URI; | import java.net.URI; | ||||||
| import java.net.URL; | import java.net.URL; | ||||||
| import java.time.temporal.Temporal; | import java.time.temporal.Temporal; | ||||||
|  | @ -615,8 +616,8 @@ public abstract class BeanUtils { | ||||||
| 	 * @return a corresponding MethodParameter object | 	 * @return a corresponding MethodParameter object | ||||||
| 	 */ | 	 */ | ||||||
| 	public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) { | 	public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) { | ||||||
| 		if (pd instanceof GenericTypeAwarePropertyDescriptor typeAwarePd) { | 		if (pd instanceof GenericTypeAwarePropertyDescriptor gpd) { | ||||||
| 			return new MethodParameter(typeAwarePd.getWriteMethodParameter()); | 			return new MethodParameter(gpd.getWriteMethodParameter()); | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			Method writeMethod = pd.getWriteMethod(); | 			Method writeMethod = pd.getWriteMethod(); | ||||||
|  | @ -787,38 +788,28 @@ public abstract class BeanUtils { | ||||||
| 		if (editable != null) { | 		if (editable != null) { | ||||||
| 			if (!editable.isInstance(target)) { | 			if (!editable.isInstance(target)) { | ||||||
| 				throw new IllegalArgumentException("Target class [" + target.getClass().getName() + | 				throw new IllegalArgumentException("Target class [" + target.getClass().getName() + | ||||||
| 						"] not assignable to Editable class [" + editable.getName() + "]"); | 						"] not assignable to editable class [" + editable.getName() + "]"); | ||||||
| 			} | 			} | ||||||
| 			actualEditable = editable; | 			actualEditable = editable; | ||||||
| 		} | 		} | ||||||
| 		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); | 		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); | ||||||
| 		Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null); | 		Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null); | ||||||
|  | 		CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ? | ||||||
|  | 				CachedIntrospectionResults.forClass(source.getClass()) : null); | ||||||
| 
 | 
 | ||||||
| 		for (PropertyDescriptor targetPd : targetPds) { | 		for (PropertyDescriptor targetPd : targetPds) { | ||||||
| 			Method writeMethod = targetPd.getWriteMethod(); | 			Method writeMethod = targetPd.getWriteMethod(); | ||||||
| 			if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) { | 			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) { | 				if (sourcePd != null) { | ||||||
| 					Method readMethod = sourcePd.getReadMethod(); | 					Method readMethod = sourcePd.getReadMethod(); | ||||||
| 					if (readMethod != null) { | 					if (readMethod != null) { | ||||||
| 						ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod); | 						if (isAssignable(writeMethod, 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) { |  | ||||||
| 							try { | 							try { | ||||||
| 								if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { | 								ReflectionUtils.makeAccessible(readMethod); | ||||||
| 									readMethod.setAccessible(true); |  | ||||||
| 								} |  | ||||||
| 								Object value = readMethod.invoke(source); | 								Object value = readMethod.invoke(source); | ||||||
| 								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { | 								ReflectionUtils.makeAccessible(writeMethod); | ||||||
| 									writeMethod.setAccessible(true); |  | ||||||
| 								} |  | ||||||
| 								writeMethod.invoke(target, value); | 								writeMethod.invoke(target, value); | ||||||
| 							} | 							} | ||||||
| 							catch (Throwable ex) { | 							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. | 	 * Inner class to avoid a hard dependency on Kotlin at runtime. | ||||||
|  | @ -896,7 +905,6 @@ public abstract class BeanUtils { | ||||||
| 			} | 			} | ||||||
| 			return kotlinConstructor.callBy(argParameters); | 			return kotlinConstructor.callBy(argParameters); | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue