Merge branch '5.3.x'
This commit is contained in:
commit
2ab871fbc3
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -686,7 +686,25 @@ public abstract class BeanUtils {
|
||||||
* from each other, as long as the properties match. Any bean properties that the
|
* 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.
|
* source bean exposes but the target bean does not will silently be ignored.
|
||||||
* <p>This is just a convenience method. For more complex transfer needs,
|
* <p>This is just a convenience method. For more complex transfer needs,
|
||||||
* consider using a full BeanWrapper.
|
* consider using a full {@link BeanWrapper}.
|
||||||
|
* <p>As of Spring Framework 5.3, this method honors generic type information
|
||||||
|
* when matching properties in the source and target objects.
|
||||||
|
* <p>The following table provides a non-exhaustive set of examples of source
|
||||||
|
* and target property types that can be copied as well as source and target
|
||||||
|
* property types that cannot be copied.
|
||||||
|
* <table border="1">
|
||||||
|
* <tr><th>source property type</th><th>target property type</th><th>copy supported</th></tr>
|
||||||
|
* <tr><td>{@code Integer}</td><td>{@code Integer}</td><td>yes</td></tr>
|
||||||
|
* <tr><td>{@code Integer}</td><td>{@code Number}</td><td>yes</td></tr>
|
||||||
|
* <tr><td>{@code List<Integer>}</td><td>{@code List<Integer>}</td><td>yes</td></tr>
|
||||||
|
* <tr><td>{@code List<?>}</td><td>{@code List<?>}</td><td>yes</td></tr>
|
||||||
|
* <tr><td>{@code List<Integer>}</td><td>{@code List<?>}</td><td>yes</td></tr>
|
||||||
|
* <tr><td>{@code List<Integer>}</td><td>{@code List<? extends Number>}</td><td>yes</td></tr>
|
||||||
|
* <tr><td>{@code String}</td><td>{@code Integer}</td><td>no</td></tr>
|
||||||
|
* <tr><td>{@code Number}</td><td>{@code Integer}</td><td>no</td></tr>
|
||||||
|
* <tr><td>{@code List<Integer>}</td><td>{@code List<Long>}</td><td>no</td></tr>
|
||||||
|
* <tr><td>{@code List<Integer>}</td><td>{@code List<Number>}</td><td>no</td></tr>
|
||||||
|
* </table>
|
||||||
* @param source the source bean
|
* @param source the source bean
|
||||||
* @param target the target bean
|
* @param target the target bean
|
||||||
* @throws BeansException if the copying failed
|
* @throws BeansException if the copying failed
|
||||||
|
|
@ -703,7 +721,10 @@ public abstract class BeanUtils {
|
||||||
* from each other, as long as the properties match. Any bean properties that the
|
* 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.
|
* source bean exposes but the target bean does not will silently be ignored.
|
||||||
* <p>This is just a convenience method. For more complex transfer needs,
|
* <p>This is just a convenience method. For more complex transfer needs,
|
||||||
* consider using a full BeanWrapper.
|
* consider using a full {@link BeanWrapper}.
|
||||||
|
* <p>As of Spring Framework 5.3, this method honors generic type information
|
||||||
|
* when matching properties in the source and target objects. See the
|
||||||
|
* documentation for {@link #copyProperties(Object, Object)} for details.
|
||||||
* @param source the source bean
|
* @param source the source bean
|
||||||
* @param target the target bean
|
* @param target the target bean
|
||||||
* @param editable the class (or interface) to restrict property setting to
|
* @param editable the class (or interface) to restrict property setting to
|
||||||
|
|
@ -721,7 +742,10 @@ public abstract class BeanUtils {
|
||||||
* from each other, as long as the properties match. Any bean properties that the
|
* 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.
|
* source bean exposes but the target bean does not will silently be ignored.
|
||||||
* <p>This is just a convenience method. For more complex transfer needs,
|
* <p>This is just a convenience method. For more complex transfer needs,
|
||||||
* consider using a full BeanWrapper.
|
* consider using a full {@link BeanWrapper}.
|
||||||
|
* <p>As of Spring Framework 5.3, this method honors generic type information
|
||||||
|
* when matching properties in the source and target objects. See the
|
||||||
|
* documentation for {@link #copyProperties(Object, Object)} for details.
|
||||||
* @param source the source bean
|
* @param source the source bean
|
||||||
* @param target the target bean
|
* @param target the target bean
|
||||||
* @param ignoreProperties array of property names to ignore
|
* @param ignoreProperties array of property names to ignore
|
||||||
|
|
@ -738,7 +762,8 @@ public abstract class BeanUtils {
|
||||||
* from each other, as long as the properties match. Any bean properties that the
|
* 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.
|
* source bean exposes but the target bean does not will silently be ignored.
|
||||||
* <p>As of Spring Framework 5.3, this method honors generic type information
|
* <p>As of Spring Framework 5.3, this method honors generic type information
|
||||||
* when matching properties in the source and target objects.
|
* when matching properties in the source and target objects. See the
|
||||||
|
* documentation for {@link #copyProperties(Object, Object)} for details.
|
||||||
* @param source the source bean
|
* @param source the source bean
|
||||||
* @param target the target bean
|
* @param target the target bean
|
||||||
* @param editable the class (or interface) to restrict property setting to
|
* @param editable the class (or interface) to restrict property setting to
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -203,8 +203,25 @@ class BeanUtilsTests {
|
||||||
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code Integer} can be copied to {@code Number}.
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
void copyPropertiesHonorsGenericTypeMatches() {
|
void copyPropertiesFromSubTypeToSuperType() {
|
||||||
|
IntegerHolder integerHolder = new IntegerHolder();
|
||||||
|
integerHolder.setNumber(42);
|
||||||
|
NumberHolder numberHolder = new NumberHolder();
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(integerHolder, numberHolder);
|
||||||
|
assertThat(integerHolder.getNumber()).isEqualTo(42);
|
||||||
|
assertThat(numberHolder.getNumber()).isEqualTo(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code List<Integer>} can be copied to {@code List<Integer>}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void copyPropertiesHonorsGenericTypeMatchesFromIntegerToInteger() {
|
||||||
IntegerListHolder1 integerListHolder1 = new IntegerListHolder1();
|
IntegerListHolder1 integerListHolder1 = new IntegerListHolder1();
|
||||||
integerListHolder1.getList().add(42);
|
integerListHolder1.getList().add(42);
|
||||||
IntegerListHolder2 integerListHolder2 = new IntegerListHolder2();
|
IntegerListHolder2 integerListHolder2 = new IntegerListHolder2();
|
||||||
|
|
@ -214,6 +231,68 @@ class BeanUtilsTests {
|
||||||
assertThat(integerListHolder2.getList()).containsOnly(42);
|
assertThat(integerListHolder2.getList()).containsOnly(42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code List<?>} can be copied to {@code List<?>}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void copyPropertiesHonorsGenericTypeMatchesFromWildcardToWildcard() {
|
||||||
|
List<?> list = Arrays.asList("foo", 42);
|
||||||
|
WildcardListHolder1 wildcardListHolder1 = new WildcardListHolder1();
|
||||||
|
wildcardListHolder1.setList(list);
|
||||||
|
WildcardListHolder2 wildcardListHolder2 = new WildcardListHolder2();
|
||||||
|
assertThat(wildcardListHolder2.getList()).isEmpty();
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(wildcardListHolder1, wildcardListHolder2);
|
||||||
|
assertThat(wildcardListHolder1.getList()).isEqualTo(list);
|
||||||
|
assertThat(wildcardListHolder2.getList()).isEqualTo(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code List<Integer>} can be copied to {@code List<?>}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void copyPropertiesHonorsGenericTypeMatchesFromIntegerToWildcard() {
|
||||||
|
IntegerListHolder1 integerListHolder1 = new IntegerListHolder1();
|
||||||
|
integerListHolder1.getList().add(42);
|
||||||
|
WildcardListHolder2 wildcardListHolder2 = new WildcardListHolder2();
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(integerListHolder1, wildcardListHolder2);
|
||||||
|
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||||
|
assertThat(wildcardListHolder2.getList()).isEqualTo(Arrays.asList(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code List<Integer>} can be copied to {@code List<? extends Number>}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void copyPropertiesHonorsGenericTypeMatchesForUpperBoundedWildcard() {
|
||||||
|
IntegerListHolder1 integerListHolder1 = new IntegerListHolder1();
|
||||||
|
integerListHolder1.getList().add(42);
|
||||||
|
NumberUpperBoundedWildcardListHolder numberListHolder = new NumberUpperBoundedWildcardListHolder();
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(integerListHolder1, numberListHolder);
|
||||||
|
assertThat(integerListHolder1.getList()).containsOnly(42);
|
||||||
|
assertThat(numberListHolder.getList()).hasSize(1);
|
||||||
|
assertThat(numberListHolder.getList().contains(Integer.valueOf(42))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code Number} can NOT be copied to {@code Integer}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void copyPropertiesDoesNotCopyeFromSuperTypeToSubType() {
|
||||||
|
NumberHolder numberHolder = new NumberHolder();
|
||||||
|
numberHolder.setNumber(Integer.valueOf(42));
|
||||||
|
IntegerHolder integerHolder = new IntegerHolder();
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(numberHolder, integerHolder);
|
||||||
|
assertThat(numberHolder.getNumber()).isEqualTo(42);
|
||||||
|
assertThat(integerHolder.getNumber()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code List<Integer>} can NOT be copied to {@code List<Long>}.
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
void copyPropertiesDoesNotHonorGenericTypeMismatches() {
|
void copyPropertiesDoesNotHonorGenericTypeMismatches() {
|
||||||
IntegerListHolder1 integerListHolder = new IntegerListHolder1();
|
IntegerListHolder1 integerListHolder = new IntegerListHolder1();
|
||||||
|
|
@ -225,6 +304,20 @@ class BeanUtilsTests {
|
||||||
assertThat(longListHolder.getList()).isEmpty();
|
assertThat(longListHolder.getList()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code List<Integer>} can NOT be copied to {@code List<Number>}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void copyPropertiesDoesNotHonorGenericTypeMismatchesFromSubTypeToSuperType() {
|
||||||
|
IntegerListHolder1 integerListHolder = new IntegerListHolder1();
|
||||||
|
integerListHolder.getList().add(42);
|
||||||
|
NumberListHolder numberListHolder = new NumberListHolder();
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(integerListHolder, numberListHolder);
|
||||||
|
assertThat(integerListHolder.getList()).containsOnly(42);
|
||||||
|
assertThat(numberListHolder.getList()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Test // gh-26531
|
@Test // gh-26531
|
||||||
void copyPropertiesIgnoresGenericsIfSourceOrTargetHasUnresolvableGenerics() throws Exception {
|
void copyPropertiesIgnoresGenericsIfSourceOrTargetHasUnresolvableGenerics() throws Exception {
|
||||||
Order original = new Order("test", Arrays.asList("foo", "bar"));
|
Order original = new Order("test", Arrays.asList("foo", "bar"));
|
||||||
|
|
@ -413,6 +506,90 @@ class BeanUtilsTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class NumberHolder {
|
||||||
|
|
||||||
|
private Number number;
|
||||||
|
|
||||||
|
public Number getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(Number number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class IntegerHolder {
|
||||||
|
|
||||||
|
private Integer number;
|
||||||
|
|
||||||
|
public Integer getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber(Integer number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class WildcardListHolder1 {
|
||||||
|
|
||||||
|
private List<?> list = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<?> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<?> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class WildcardListHolder2 {
|
||||||
|
|
||||||
|
private List<?> list = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<?> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<?> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class NumberUpperBoundedWildcardListHolder {
|
||||||
|
|
||||||
|
private List<? extends Number> list = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<? extends Number> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<? extends Number> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class NumberListHolder {
|
||||||
|
|
||||||
|
private List<Number> list = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<Number> getList() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setList(List<Number> list) {
|
||||||
|
this.list = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static class IntegerListHolder1 {
|
private static class IntegerListHolder1 {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue