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 c0ab530a40..a6730b3243 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -19,6 +19,7 @@ package org.springframework.beans; import java.beans.PropertyDescriptor; import java.beans.PropertyEditor; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -45,6 +46,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.core.KotlinDetector; import org.springframework.core.MethodParameter; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -681,7 +683,8 @@ public abstract class BeanUtils { } /** - * Copy the property values of the given source bean into the given target bean. + * Copy the property values of the given source bean into the given target bean + * and ignored if *

Note: The source and target classes do not have to match or even be derived * from each other, as long as the properties match. Any bean properties that the * source bean exposes but the target bean does not will silently be ignored. @@ -714,9 +717,17 @@ public abstract class BeanUtils { if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { + Field sourcefield = ReflectionUtils.findField(source.getClass(), sourcePd.getName()); + Field targetfield = ReflectionUtils.findField(target.getClass(), targetPd.getName()); + + TypeDescriptor sourceTypeDescriptor = new TypeDescriptor(sourcefield); + TypeDescriptor targetTypeDescriptor = new TypeDescriptor(targetfield); + Method readMethod = sourcePd.getReadMethod(); + if (readMethod != null && - ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { + ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) && + sourceTypeDescriptor.getResolvableType().equals(targetTypeDescriptor.getResolvableType())) { try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); @@ -724,7 +735,7 @@ public abstract class BeanUtils { Object value = readMethod.invoke(source); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); - } + } writeMethod.invoke(target, value); } catch (Throwable ex) { diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java index 907cbbb9b2..50dd7a3c74 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java @@ -24,6 +24,7 @@ import java.net.URI; import java.net.URL; import java.time.DayOfWeek; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; @@ -336,6 +337,49 @@ class BeanUtilsTests { private void assertSignatureEquals(Method desiredMethod, String signature) { assertThat(BeanUtils.resolveSignature(signature, MethodSignatureBean.class)).isEqualTo(desiredMethod); } + + @Test + void testCopiedParametersType() { + + A a = new A(); + a.getList().add(42); + B b = new B(); + + BeanUtils.copyProperties(a, b); + + assertThat(a.getList()).containsOnly(42); + + b.getList().forEach(n -> assertThat(n).isInstanceOf(Long.class)); + assertThat(b.getList()).isEmpty(); + + } + + class A { + + private List list = new ArrayList<>(); + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + } + class B { + + private List list = new ArrayList<>(); + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + } @SuppressWarnings("unused")