Beanutils.copyProperties
最简单的使用spring的Beanutils
UserPO source = userRepository.getOne(1L);//name:source id:2
UserPO target= new UserPO();
BeanUtils.copyProperties(source, target);
//这实际上是使用了调用了source的get方法 然后再调用target的set方法. 可以看作是一个浅拷贝
public class UserPO {
private Long id;
private String name;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public Optional<String> getName() {
return Optional.ofNullable(name);
}
}
问题
但是你会发现这个Beanutils.copyProperties并不能将source的 name属性赋给target. 原因如下
BeanUtils使用的Introspector(内省). Introspector.getBeanInfo(obj.getClass()).getPropertyDescriptors() 将会返回一个PropertyDescriptor数组.
PropertyDescriptor对象就是你的get方法(和是否有这个属性无关)对应的一个属性值的详情(对应的get set方法) 举例
public class User {
private String name;
public String getId(){}
}
//getPropertyDescriptors()返回的数组里面不会有name但是会有id
因此一开始的UserPO得到的PropertyDescriptor 对象只有一个类型为 Long 的id 和一个类型为Optional 的name.而 setName方法是String类型的因此 这个name 的PropertyDescriptor 对象不存在set方法.
所以只能自己用内省简单的写一个方法:
public static<T> T copyBean(Object obj , T target){
if(obj == null || target == null){
return null;
}
try {
//少用getBeanInfo的第二个参数 性能极差
PropertyDescriptor[] objDescriptors = Introspector.getBeanInfo(obj.getClass()).getPropertyDescriptors();
MethodDescriptor[] targetMethodDescriptor = Introspector.getBeanInfo(target.getClass()).getMethodDescriptors();
for (PropertyDescriptor property : objDescriptors) {
String key = property.getName();
// 过滤class属性
if (!key.equals("class") && property.getReadMethod() != null) {
// 将属性第一位大写
String setName = "set"+StringUtil.toUpStart(key);
Object value = property.getReadMethod().invoke(obj);
Optional<MethodDescriptor> targetSetMethod = Arrays.stream(targetMethodDescriptor).filter(a -> a.getName().equals(setName)).findFirst();
if(targetSetMethod.isPresent()){
if(value instanceof Optional){
value = ((Optional) value).orElse(null);
}
targetSetMethod.get().getMethod().invoke(target,value);
}
}
}
} catch (Exception e) {
log.warn("copyBean error",e);
}
return target;
}