CGLIB深拷贝BeanCopier的使用和详解
我们本次讲的是CGLIB的BeanCopier工具包,当我们需要拷贝大量的数据,使用这个是最快的,当拷贝少量对象时,和其它的拷贝工具类速度也差不多,现在CGLIB也并入Spring,所以在Spring项目中可以直接使用它,不需要添加其他maven。
在使用他的时候,我们需要先创建一个BeanCopier对象,源代码如下:
public static BeanCopier create(Class source, Class target, boolean useConverter) {
BeanCopier.Generator gen = new BeanCopier.Generator();
gen.setSource(source);
gen.setTarget(target);
gen.setUseConverter(useConverter);
return gen.create();
}
create参数:
-
第一个参数source:我们要拷贝的对象
-
第二个参数target:我们要拷贝成什么样的对象
-
第三个参数useConverter:用户控制转换器,在下文对这个参数做解释,因为CGLIB是动态代理的,所以有控制反转
useConverter控制权限转换:
这个是个用户控制转换器,如果选择为false,它会对拷贝的对象和被拷贝的对象的类型进行判断,如果类型不同就不会拷贝,如果要使他会拷贝,就需要设置为true,自己拿到控制权自己对其进行处理,一般情况下我们都是使用的false
@Data
public class User {
private Integer id;
private String name;
}
@Data
public class UserDTO {
private String id;
private String name;
}
其中的id类型是不同的。我们来试试useConverter设置为false的时候拷贝,看看拷贝结果
User user = new User();
user.setId(1);
user.setName("测试");
UserDTO userDTO = BeanCopierUtils.copy(user, UserDTO.class);
System.out.println(user);
System.out.println(userDTO);
从控制台结果可以看出,拷贝的对象的id是空的,因为类型不同:
User(id=1, name=测试)
UserDTO(id=null, name=测试)
如果我们要不同类型都拷贝上去,就要设置useConverter的控制权设置为true,那么我们需要重写Convert类,重写代码如下:
import org.springframework.cglib.core.Converter;
public class ConverterSetting implements Converter {
@Override
public Object convert(Object o, Class aClass, Object o1) {
if (o instanceof Enum) {
return o;
}else {
return o.toString();
}
}
}
convert参数:
- 第一个参数o:指的是你实体类的值
- 第二个参数aClass:指的是你要转换成什么样的类型
- 第三个参数o1:指的是你实体类的set方法
使用convert为true的代码:
private static void copy(Object str, Object target) {
BeanCopier beanCopier = BeanCopier.create(str.getClass(), target.getClass(), true);
ConverterSetting converterSetting = new ConverterSetting();
beanCopier.copy(str, target, converterSetting);
}
执行后的结果:
User(id=1, name=测试)
UserDTO(id=1, name=测试)
在完整工具类中,我定义了2个实例化的实体类进行深拷贝,代码如下:
public static void copy(Object str, Object target) {
if (str == null) {
return;
}
// 用来判断空指针异常
Objects.requireNonNull(target);
BeanCopier beanCopier = BeanCopier.create(str.getClass(), target.getClass(), false);
beanCopier.copy(str, target, null);
}
实体类如下:
@Data
public class UserDTO {
private Integer id;
private String name;
private String test;
}
@Data
public class User {
private Integer id;
private String name;
}
Service代码层:
User user = new User();
user.setId(1);
user.setName("测试");
UserDTO userDTO = new UserDTO();
userDTO.setName("这是userDTO的值,会被覆盖");
userDTO.setTest("userDTO");
BeanCopierUtils.copy(user, userDTO);
System.out.println(userDTO);
控制台结果,因为是深拷贝,所以把实体类类型和名称相等的值进行赋值,控制台结果如下:
UserDTO(id=1, name=测试, test=userDTO)
Map缓存提高速度:
因为我们使用BeanCopier拷贝,经常会重复拷贝对象,因此会重复耗费时间,所以放到Map对象内,可以大大缩短时间,具体代码可以参照工具类完整代码
工具类完整代码:
import org.springframework.cglib.beans.BeanCopier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class BeanCopierUtils {
// 创建一个map来存储BeanCopier
private static final Map<String, BeanCopier> beanCopierMap = new HashMap<>();
/**
* 深拷贝,我们可以直接传实例化的拷贝对象和被实例化的拷贝对象进行深拷贝
*
* @param str
* @param target
*/
public static void copy(Object str, Object target) {
if (str == null) {
return;
}
// 用来判断空指针异常
Objects.requireNonNull(target);
String key = getKey(str, target);
BeanCopier beanCopier;
// 判断键是否存在,不存在就将BeanCopier插入到map里,存在就直接获取
if (!beanCopierMap.containsKey(key)) {
beanCopier = BeanCopier.create(str.getClass(), target.getClass(), false);
beanCopierMap.put(key, beanCopier);
} else {
beanCopier = beanCopierMap.get(key);
}
beanCopier.copy(str, target, null);
}
/**
* 深拷贝
*
* @param str
* @param tClass
* @param <T>
* @return
*/
public static <T> T copy(Object str, Class<T> tClass) {
if (str == null) {
return null;
}
// 用来判断空指针异常
Objects.requireNonNull(tClass);
T dest = null;
try {
dest = tClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
copy(str, dest);
return dest;
}
/**
* List深拷贝
*
* @param strList
* @param tClass
* @param <T>
* @param <S>
* @return
*/
public static <T, S> List<T> copyList(List<S> strList, Class<T> tClass) {
// 判断空指针异常
Objects.requireNonNull(tClass);
return strList.stream().map(src -> copy(src, tClass)).collect(Collectors.toList());
}
/**
* 获取Map Key
*
* @param str
* @param target
* @return
*/
private static String getKey(Object str, Object target) {
return str.getClass().getName() + target.getClass().getName();
}
}