-
开头
Bean 拷贝的工具有很多,有 Apache BeanUtils、Spring BeanUtils、Mapstruct、cglib BeanCopier 等等
Apache 和 Spring 的 BeanUtils 效率并不是我想要的,而使用 Mapstruct 又太繁琐了
那有没有简单易用的,而且效率还比较高的呢?这就是我这篇文章要讲述的主要内容
-
Bean 拷贝工具的区别
BeanUtils 通过反射进行属性赋值操作
BeanCopier 使用 cglib 动态代理生成带有 get/set 方法的类进行赋值
BeanCopier 是生成字节码执行,所以 BeanCopier 的性能接近手写
Mapstruct 类似与 lombok,在编译期间帮你生成一个实现类,性能最好
-
BeanCopier 使用
创建两个类,模拟转换
@Data
public class AuthUserVO {
private String name;
private Integer age;
}
@Data
public class AuthUser {
private String name;
private Integer age;
}
public static void main(String[] args) {
AuthUserVO authUserVO = new AuthUserVO();
authUserVO.setName("张三");
authUserVO.setAge(25);
AuthUser authUser = new AuthUser();
BeanCopier beanCopier = BeanCopier.create(authUserVO.getClass(), authUser.getClass(), false);
beanCopier.copy(authUserVO, authUser, null);
System.out.println(authUser);
}
输出结果
BeanCopier 只能拷贝类型相同并且属性名也相同的,但是可以自己实现转换规则,本文不详细讲述此用法
-
BeanCopier 实战使用
封装 BeanCopier 工具类
/**
* Bean 转换高性能工具
* 如果source或者targetSupplier只要有一个为null就返回null
* 调用方如果把null进行转换,那就是想转换为null,为不为空应该由调用方自己负责
*/
public class ConvertUtil {
private ConvertUtil() {}
/**
* BeanCopier缓存(享元模式)
*/
private static final Map<CopierIdentity, BeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();
/**
* 将source对象的属性拷贝到target对象中去
*
* @param source source对象
* @param target target对象
*/
public static void copyProperties(Object source, Object target) {
if (null == source || null == target) {
return;
}
// 作为 Key,
CopierIdentity identity = CopierIdentity.create(source.getClass(), target.getClass());
// 查看 concurrentHashMap 的实现方法介绍可以得知该方法为原子方法,保证了线程安全
// 复用 BeanCopier 对象,因为创建比较耗时
BeanCopier beanCopier = BEAN_COPIER_MAP.computeIfAbsent(identity, v -> BeanCopier.create(source.getClass(), target.getClass(), false));
beanCopier.copy(source, target, null);
}
public static <S, T> T single(S source, Supplier<T> targetSupplier) {
return single(source, targetSupplier, null);
}
/**
* 转换对象
*
* @param source 源对象
* @param targetSupplier 目标对象供应方
* @param callBack 回调方法
* @param <S> 源对象类型
* @param <T> 目标对象类型
* @return 目标对象
*/
public static <S, T> T single(S source, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
if (null == source || null == targetSupplier) {
return null;
}
T target = targetSupplier.get();
copyProperties(source, target);
if (null != callBack) {
callBack.callBack(source, target);
}
return target;
}
public static <S, T> List<T> list(List<S> sources, Supplier<T> targetSupplier) {
return list(sources, targetSupplier, null);
}
/**
* 转换对象
*
* @param sources 源对象list
* @param targetSupplier 目标对象供应方
* @param callBack 回调方法
* @param <S> 源对象类型
* @param <T> 目标对象类型
* @return 目标对象list
*/
public static <S, T> List<T> list(List<S> sources, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
List<T> list = new ArrayList<>();
if (null == sources || null == targetSupplier) {
return list;
}
for (S source : sources) {
list.add(single(source, targetSupplier, callBack));
}
return list;
}
/**
* 回调接口
*
* @param <S> 源对象类型
* @param <T> 目标对象类型
*/
@FunctionalInterface
public interface ConvertCallBack<S, T> {
/**
* 回调方法
* @param t 目标对象
* @param s 源对象
*/
void callBack(S t, T s);
}
private static class CopierIdentity {
private Class<?> source;
private Class<?> target;
private static CopierIdentity create(Class<?> source, Class<?> target) {
CopierIdentity identity = new CopierIdentity();
identity.source = source;
identity.target = target;
return identity;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CopierIdentity)) {
return false;
}
CopierIdentity identity = (CopierIdentity) o;
return Objects.equals(source, identity.source) && Objects.equals(target, identity.target);
}
@Override
public int hashCode() {
return Objects.hash(source, target);
}
}
}
单个对象转换
- 使用方式1
public static void main(String[] args) {
AuthUserVO authUserVO = new AuthUserVO();
authUserVO.setName("张三");
authUserVO.setAge(25);
AuthUser authUser = new AuthUser();
ConvertUtil.copyProperties(authUserVO, authUser);
System.out.println(authUser);
}
- 使用方式2
public static void main(String[] args) {
AuthUserVO authUserVO = new AuthUserVO();
authUserVO.setName("张三");
authUserVO.setAge(25);
AuthUser authUser = ConvertUtil.single(authUserVO, AuthUser::new);
System.out.println(authUser);
}
- 使用方式3
使用 Java 8 的函数式编程解决字段类型不一样或者字段属性名不一样的问题,工具类已封装
创建两个类,模拟转换
@Data
public class AuthUserVO {
private String name;
private Integer age;
}
@Data
public class AuthUser {
private String username;
private int age;
}
public static void main(String[] args) {
AuthUserVO authUserVO = new AuthUserVO();
authUserVO.setName("张三");
authUserVO.setAge(25);
AuthUser authUser = ConvertUtil.single(authUserVO, AuthUser::new, (s, t) -> {
t.setUsername(s.getName());
t.setAge(s.getAge());
});
System.out.println(authUser);
}
输出结果
转换集合
- 使用方式1
public static void main(String[] args) {
List<AuthUserVO> authUserVOList = new ArrayList<>();
AuthUserVO authUserVO = new AuthUserVO();
authUserVO.setName("张三");
authUserVO.setAge(25);
authUserVOList.add(authUserVO);
List<AuthUser> authUserList = ConvertUtil.list(authUserVOList, AuthUser::new);
System.out.println(authUserList);
}
- 使用方式2
public static void main(String[] args) {
List<AuthUserVO> authUserVOList = new ArrayList<>();
AuthUserVO authUserVO = new AuthUserVO();
authUserVO.setName("张三");
authUserVO.setAge(25);
authUserVOList.add(authUserVO);
List<AuthUser> authUserList = ConvertUtil.list(authUserVOList, AuthUser::new, (s, t) -> {
t.setUsername(s.getName());
t.setAge(s.getAge());
});
System.out.println(authUserList);
}
输出结果