1. Null values are copied
Model1
import java.time.LocalDateTime;
import java.util.List;
/**
* com.example
* Description:
*
* @author jack
* @date 2021/6/21 7:21 下午
*/
@Data
public class Model1 {
private String name;
private String password;
private int age;
private boolean vip;
private LocalDateTime dateTime;
private List<String> strList;
}
Copy the code
Model2
import java.time.LocalDateTime;
import java.util.List;
/**
* com.example
* Description:
*
* @author jack
* @date2021/6/21 7:22pm */
@Data
public class Model2 {
private String name;
private String password;
private Integer age;
private Boolean vip;
private LocalDateTime dateTime;
private List<Object> strList;
}
Copy the code
Null value copy test BeanUtilTest
import org.springframework.beans.BeanUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* com.example
* Description:
*
* @author jack
* @date2021/6/21 7:24pm */
public class BeanUtilTest {
public static void main(String[] args) {
Model1 model1 = new Model1();
model1.setName("Zhang");
model1.setAge(22);
List<String> strList = new ArrayList<>();
strList.add("I'm John.");
model1.setStrList(strList);
Model2 model2 = newModel2(); model2.setDateTime(LocalDateTime.now()); BeanUtils.copyProperties(model1, model2); System.out.println(model2); }}Copy the code
The dateTime attribute in Model2 becomes NULL
2.Boolean data may be lost during copy
Change the VIP field in Model1 and Model2 to isVip, and keep the data type unchanged. One is the basic type Boolean and the other is the wrapper type Boolean
private boolean isVip;
Copy the code
The test case is unchanged
It is found that the isVip in Model1 has not been copied to Model2
This may be found in the source code
// sourcePd is not obtained by targetpd.getName (), where targetpD.getName () is isVip
getPropertyDescriptor(source.getClass(), targetPd.getName())
Copy the code
Keep going down
In the org. Springframework. Beans. CachedIntrospectionResults# getPropertyDescriptor, through isVip attributes of target object to find the source, the and couldn’t find it, The isVip field in the source is resolved to Vip
Go straight down to line 513 in the java.beans.Introspector#getTargetPropertyInfo
If the property type is Boolean, the property name starts with is, and the preceding is is removed
So the Boolean isXXX() will parse to XXX, so be careful when copying.
Solution:
- Modify the
Model2
In theprivate Boolean isVip
Properties forVip
Because theModel1
In theprivate boolean isVip
Is resolved as aVip
the source
andtarget
The attribute name must be consistent with the attribute type
3. Generic type copy problems
When the target of the attribute name and attribute name of the source, can be copied, this situation may arise in the development, when use of the target attribute, may be an error. The Java lang. ClassCastException
Solution: when copying, ignore the properties with the same name but different types
BeanUtils org. Springframework. Beans. BeanUtils. CopyProperties for encapsulation, ignore null values, as a different but ignore the attribute name
package com.example;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.StringUtils;
import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* com.youzan.fx.trade.test
* Description:
*
* @author jack
* @date 2021/6/21 5:07 下午
*/
public class BeanUtils {
private static final String BYTE = "byte";
private static final String SHORT = "short";
private static final String CHAR = "char";
private static final String BOOLEAN = "boolean";
private static final String INT = "int";
private static final String LONG = "long";
private static final String FLOAT = "float";
private static final String DOUBLE = "double";
/** * Ignore null values, ignore properties with the same name but different types **@param source source
* @param target target
*/
public static void copyPropertiesIgnoreNull(Object source, Object target) {
org.springframework.beans.BeanUtils.copyProperties(source, target, getIgnoreFieldNames(source, target));
}
private static String[] getIgnoreFieldNames(Object source, Object target) {
final BeanWrapper sourceWrapper = new BeanWrapperImpl(source);
final BeanWrapper targetWrapper = new BeanWrapperImpl(target);
PropertyDescriptor[] sourcePds = sourceWrapper.getPropertyDescriptors();
PropertyDescriptor[] targetPds = targetWrapper.getPropertyDescriptors();
Map<String, String> targetMap = new HashMap<>(targetPds.length);
for (PropertyDescriptor targetPd : targetPds) {
targetMap.put(targetPd.getName(), getFieldTypeName(targetPd.getReadMethod().getGenericReturnType().getTypeName()));
}
Set<String> ignoreFieldSet = new HashSet<>();
for (PropertyDescriptor sourcePd : sourcePds) {
Object srcValue = sourceWrapper.getPropertyValue(sourcePd.getName());
if (srcValue == null) {
ignoreFieldSet.add(sourcePd.getName());
} else {
String fieldTypeName = targetMap.get(sourcePd.getName());
if(StringUtils.hasText(fieldTypeName) && ! Objects.equals(fieldTypeName, getFieldTypeName(sourcePd.getReadMethod().getGenericReturnType().getTypeName()))) { ignoreFieldSet.add(sourcePd.getName()); }}}return ignoreFieldSet.toArray(new String[0]);
}
private static String getFieldTypeName(String fieldTypeName) {
String typeName = "";
switch (fieldTypeName) {
case BYTE:
typeName = Byte.class.getTypeName();
break;
case SHORT:
typeName = Short.class.getTypeName();
break;
case CHAR:
typeName = Character.class.getTypeName();
break;
case BOOLEAN:
typeName = Boolean.class.getTypeName();
break;
case INT:
typeName = Integer.class.getTypeName();
break;
case LONG:
typeName = Long.class.getTypeName();
break;
case FLOAT:
typeName = Float.class.getTypeName();
break;
case DOUBLE:
typeName = Double.class.getTypeName();
break;
default:
break;
}
if (StringUtils.hasText(typeName)) {
return typeName;
}
returnfieldTypeName; }}Copy the code