改造BeanUtils,優雅實現List數據拷貝
BeanUtils.copyProperties();確實為我們做了很多事情,雖然不能完美完成深拷貝,但是對于 po、vo、dto 的拷貝已經足夠用了。可還是有一些不夠完美的地方。
不足幾點如下:
①. 不能拷貝 list,而拷貝 list 的情況又大量存在,因此會有許多重復代碼。
for (S source : sources) {
T target = new T();
copyProperties(source, target);
list.add(target);
}
②. 有一些簡單的查詢,僅僅需要轉換一下 vo 也需要 new Vo()。
public Vo findById(Integer id) {
Vo vo = new Vo();
Po po = dao.findById(id);
copyProperties(po, vo);
return vo;
}
③. 這種拷貝方式是沒有返回值的,jdk8 支持 stream() 操作之后,支持不是很友好,不方便 lambda 表達式的使用,因此我們決定通過集成 BeanUtils 類,自己造一個方便用的輪子。
使用
我們將新創建一個輪子 BeanConvertUtils,使用如下,當我們要轉換 po、vo 時,只需要:
// 使用前
public Vo findById(Integer id) {
Vo vo = new Vo();
Po po = dao.findById(id);
copyProperties(po, vo);
return vo;
}
// 使用后
public Vo findById(Integer id) {
return BeanConvertUtils.converTo(dao.findById(id), Vo::new);
}
// 使用后,通過lambda表達式特殊處理個別字段
public Vo findById(Integer id) {
return BeanConvertUtils.converTo(dao.findById(id), Vo::new,
(s, t) -> t.setName(s.getName))
);
}
當我們要拷貝 list 的時候也很簡單:
// 使用前
public List<Vo> findAll() {
List<Vo> vos = new ArrayList();
List<Po> pos = dao.findAll();
for (Po po : Pos) {
Vo vo = new Vo();
BeanUtis.copyProperties(po, vo);
vos.add(vo);
}
return vos;
}
// 使用后
public List<Vo> findAll() {
return BeanConvertUtils.converToList(dao.findAll(), Vo::new)
}
// 同樣支持自定義lambda
public List<Vo> findAll() {
return BeanConvertUtils.converToList(dao.findAll(), Vo::new,
(s, t) -> t.setName(s.getName))
)
}
代碼如下:
/**
* 轉換對象工具
*
*/
public class BeanConvertUtils extends BeanUtils {
public static <S, T> T convertTo(S source, Supplier<T> targetSupplier) {
return convertTo(source, targetSupplier, null);
}
/**
* 轉換對象
*
* @param source 源對象
* @param targetSupplier 目標對象供應方
* @param callBack 回調方法
* @param <S> 源對象類型
* @param <T> 目標對象類型
* @return 目標對象
*/
public static <S, T> T convertTo(S source, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
if (null == source || null == targetSupplier) {
return null;
}
T target = targetSupplier.get();
copyProperties(source, target);
if (callBack != null) {
callBack.callBack(source, target);
}
return target;
}
public static <S, T> List<T> convertListTo(List<S> sources, Supplier<T> targetSupplier) {
return convertListTo(sources, targetSupplier, null);
}
/**
* 轉換對象
*
* @param sources 源對象list
* @param targetSupplier 目標對象供應方
* @param callBack 回調方法
* @param <S> 源對象類型
* @param <T> 目標對象類型
* @return 目標對象list
*/
public static <S, T> List<T> convertListTo(List<S> sources, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
if (null == sources || null == targetSupplier) {
return null;
}
List<T> list = new ArrayList<>(sources.size());
for (S source : sources) {
T target = targetSupplier.get();
copyProperties(source, target);
if (callBack != null) {
callBack.callBack(source, target);
}
list.add(target);
}
return list;
}
/**
* 回調接口
*
* @param <S> 源對象類型
* @param <T> 目標對象類型
*/
@FunctionalInterface
public interface ConvertCallBack<S, T> {
void callBack(S t, T s);
}
}
性能
由于只是 BeanUtils 的一個封裝,跟原來的代碼性能幾乎差不多,如果要說差一點也沒錯,畢竟多了一層函數堆棧的調用,但是基本可以忽略不計。主要的性能還是由 BeanUtils 決定。
提醒
不知道大家對這個 BeanConvertUtils 工具類感覺怎么樣,自己在項目中倒是大量使用,也很方便。
但是有兩點要提醒:
- 此方法依舊不能解決深層次的深拷貝問題,詳細的可以 google 一下 BeanUtils 的深拷貝問題。
- 如果 source 或者 targetSupplier 只要有一個為 null,本工具類不像 BeanUtils 一樣拋出異常,而是返回 null,因為筆者認為調用方如果把 null 進行準換,那就是想轉換為 null,為不為空應該由調用方自己負責。