1、场景:微服务层与层之间进行对象的拷贝
微服务里面为了代码层次结构清晰,一般会进行分层,DAO层,Servic层,Controller。不同层使用不同后缀对象来进行数据传递
- DAO层一般会使用 DO 对象来进行传递
- Service层一般会使用 DTO 对象来进行传递
- Controller层一般会使用VO 对象来进行传递
层与层之前进行传递数据的时候,往往对象之间的属性类似,就会重写对象的clone方法,进行对象的拷贝
比如将 DO 对象转换为 DTO 对象,或者 DTO 对象转换为 VO 对象。如何优化高性能的实现对象的拷贝呢?
2、问题:如何优化高性能的实现对象的拷贝呢?将DO对象转换为DTO对象,或者将DTO对象转换为VO对象?
3.1 常见的方法
-
apache的BeanUtils:反射
-
apache的PropertyUtils:反射
-
spring的BeanUtils:反射
3.2 存在的问题
上述工具类都是采用反射的方式,来进行数据的拷贝,但是反射的性能是很差的,之前已经有很多人对这几种Bean属性拷贝工具做过了性能测试了,但是发现上面三种的性能都是很差的,不建议使用。
3.3 解决方案
答案:采用cglib的BeanCopier来进行对象的拷贝,底层原理是使用动态代理,性能很高,达到上面反射方式的几十倍甚至数百倍
4、使用BeanCopier来进行对象的拷贝(实战)
4.1 引入依赖
<!--cglib的BeanCopier需要的依赖-->
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-commons</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-util</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.4</version>
</dependency>
4.2 3 BeanCopier工具类的编写
/**
* BeanCopier工具类
*
* @version:v1.0
*/
public class BeanCopierUtils {
/**
* BeanCopier 缓存Map
*/
public static Map<String, BeanCopier> beanCopierCacheMap = new HashMap<>();
/**
* @param source 源对象
* @param target 目标对象
* @return org.springframework.cglib.beans.BeanCopier
* 将source对象的属性拷贝到target对象中去
* @date 2021/2/6
*/
public static void copyProperties(Object source, Object target) throws Exception {
/** Map 缓存Key*/
String cacheKey = source.getClass().toString() + target.getClass().toString();
BeanCopier beanCopier = null;
// 双重交易锁,防止并发问题发生
if (!beanCopierCacheMap.containsKey(cacheKey)) {
synchronized (BeanCopierUtils.class) {
if (!beanCopierCacheMap.containsKey(cacheKey)) {
beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
} else {
beanCopier = beanCopierCacheMap.get(cacheKey);
}
}
} else {
beanCopier = beanCopierCacheMap.get(cacheKey);
}
assert beanCopier != null;
beanCopier.copy(source, target, null);
}
}
4.2.4 DO类和DTO类
/**
*
* @Version:v1.0
*/
public class PriorityDO {
/**
* 主键
*/
private Long id;
...
...
/**
* 创建时间
*/
private Date gmtCreate;
/**
* 更新时间
*/
private Date gmtModified;
/**
* 克隆方法
*
* @param clazz 目标class对象
* @return T 克隆后的对象
* @date 2021/2/5
*/
public <T> T clone(Class<T> clazz) {
T target = null;
try {
target = clazz.newInstance();
BeanCopierUtils.copyProperties(this, target);
} catch (Exception e) {
logger.error("PriorityNode.clone error,clazz : {}", clazz, e);
}
return target;
}
}
/**
* 权限节点
*
* @Version:v1.0
*/
public class PriorityDTO {
/**
* 主键
*/
private Long id;
...
...
/**
* 创建时间
*/
private Date gmtCreate;
/**
* 更新时间
*/
private Date gmtModified;
/**
* 克隆方法
*
* @param clazz 目标class对象
* @return T 克隆后的对象
* @date 2021/2/5
*/
public <T> T clone(Class<T> clazz) {
T target = null;
try {
target = clazz.newInstance();
BeanCopierUtils.copyProperties(this, target);
} catch (Exception e) {
logger.error("PriorityNode.clone error,clazz : {}", clazz, e);
}
return target;
}
}
4.2.5 对象拷贝
public static void main(String[] args) throws Exception {
PriorityDO source = new PriorityDO();
// 给resource设置各种属性
...
// 拷贝属性到指定对象类型里面
PriorityDTO target = source.clone(PriorityDTO.class);
System.out.println(JSON.toJSONString(target));
}