在实际的开发中,存在Vo对象,Dao对象,DTO对象的部分属性复制的问题
比较笨的办法 A.setxxx(B.getXXX),费时又费力,怎么解决这个问题呢?
推荐使用两种对象属性复制的办法:
对象属性拷贝的两种方式:
1、BeanUtils(注意BeanUtils使用的是spring的Beanutils的)
2、BeanCopier
先准备三个Vo对象,Dao对象,Dto对象,后面我们对这三个对象进行拷贝和复制。
@Data
@AllArgsConstructor
public class UserDo {
private int id;
private String userName;
private LocalDateTime gmtBroth;
private BigDecimal balance;
}
@Data
public class UserDto {
private int id;
private String userName;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVo {
private int id;
private String userName;
private String gmtBroth;
private String agevo;
}
第一种方式使用BeanUtils进行拷贝
import org.springframework.beans.BeanUtils;
@Slf4j
public class BeanCopyTest {
public static void main(String[] args) {
UserDo userDo=new UserDo(1,"sunyuhau", LocalDateTime.now(),new BigDecimal(100L));
log.info("拷贝前=="+userDo);
UserVo userVo=new UserVo();
BeanUtils.copyProperties(userDo,userVo);
log.info("拷贝后==="+userVo);
}
}
输出结果为:
但是这种方法耗时比较长,线上有点不能满足需求。所以引出了第二种方案:
BeanCopier
long startTime2=System.currentTimeMillis();
UserDo userDo1=DataUtil.createData();
log.info("拷贝前 userDo1:{}",userDo1);
//将userDo拷贝到userdto,false代表是不使用自定义转化器
BeanCopier beanCopier=BeanCopier.create(UserDo.class,UserDto.class,false);
UserDto userDto=new UserDto();
//不使用conberter方式,仅对两个bean对象属性名和类型完全相同的变量进行拷贝。
beanCopier.copy(userDo1,userDto,null);
log.info("拷贝后 :userDto:{}",userDto);
log.info("耗时:"+(System.currentTimeMillis()-startTime2));
BeanCopier 有两种方式,一个是conberter,一个是不使用自定义的conberter,上面的不使用自定义器的。
long startTime3=System.currentTimeMillis();
UserDo userDo2=DataUtil.createData();
log.info("拷贝前:{}",userDo2);
BeanCopier.create(UserDo.class,UserVo.class,true);
UserDo userDo3=DataUtil.createData();
log.info("拷贝前:userDo:{}",userDo3);
BeanCopier beanCopier1 = BeanCopier.create(UserDo.class, UserVo.class, true);
UserConverter userConverter=new UserConverter();
UserVo uservo3=new UserVo();
beanCopier1.copy(userDo3,uservo3,userConverter);
log.info("拷贝后==userDo:{}",uservo3);
log.info("耗时3:"+(System.currentTimeMillis()-startTime3));
/**
* 自定义的user对象的转化器
* 一旦使用converter,BeanCopier只使用converter定义的规则去拷贝,所以在convert()方法中要考虑所有的属性
* 并且converter会是对象拷贝速度变慢。
*
*/
public class UserConverter implements Converter {
DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
@Override
public Object convert(Object o, Class aClass, Object o1) {
if(o instanceof Integer){
return o;
}else if(o instanceof LocalDateTime){
LocalDateTime date=(LocalDateTime)o;
return dateTimeFormatter.format(date);
}else if(o instanceof BigDecimal){
BigDecimal bigDecimal=(BigDecimal)o;
return bigDecimal.toPlainString();
}
return o;
}
}
不管使不使用自定义的转化器,都有还有一个弊端,就是每次都初始化 BeanCopier beanCopier=BeanCopier.create(UserDo.class,UserDto.class,false);
BeanCopier beanCopier1 = BeanCopier.create(UserDo.class, UserVo.class, true);
这个点是不必须的。所以可以将这个初始化到内存中,这样就没有必要每次都生成 BeanCopier。
具体的做法如下:
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanCopier;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class BeanCopierWithCacheUtil {
private static ConcurrentHashMap<String,BeanCopier> BEAN_COPIERS = new ConcurrentHashMap<>() ;
static void beanCopierWithCache() {
List<UserDo> userDOList = DataUtil.createDataList(10000);
long start = System.currentTimeMillis();
List<UserDto> userDtos = new ArrayList<>();
userDOList.forEach(userDo -> {
UserDto userDTO = new UserDto();
//将userDo对象的参数,复制到userDTO对象
copy(userDo, userDTO);
userDtos.add(userDTO);
});
log.info("BeanCopier 加缓存后 costTime: {}ms", System.currentTimeMillis() - start);
}
/**
* 将不用类型转化的copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false) 放入内存
* 第一次初始化的时候将BeanCopier,初始到内存中,下次再次使用时。直接使用。
* @param srcObj
* @param destObj
*/
public static void copy(Object srcObj, Object destObj) {
String key = genKey(srcObj.getClass(), destObj.getClass());
BeanCopier copier = null;
if (!BEAN_COPIERS.containsKey(key)) {
copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false);
BEAN_COPIERS.put(key, copier);
} else {
copier = BEAN_COPIERS.get(key);
}
copier.copy(srcObj, destObj, null);
}
/**
* 将转化的类的名字和被转化类型的名字作为key,保存到内存中
* @param srcClazz
* @param destClazz
* @return
*/
private static String genKey(Class<?> srcClazz, Class<?> destClazz) {
return srcClazz.getName() + destClazz.getName();
}
}
log.info("+++++++++++BeanCopier放在缓存中+++++++++++++++");
//注意第一次是初始化BeanCopier,时间会比较长
BeanCopierWithCacheUtil beanCopierWithCacheUtil=new BeanCopierWithCacheUtil();
beanCopierWithCacheUtil.beanCopierWithCache();
这样耗时就会更短,转化的效率就更高了。