我們本次講的是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();
}
}