CGlib 之 Bean拷貝(BeanCopier)

      BeanCopier 顧名思義這是一個快捷的 bean 類復制工具類,應用常見通常為 2 個實體對象存在大于 50% 類似 屬性 如果 get set 賦值又臭又長,當然這是最佳性能。事實上如果該實體存在大于 10 個以上屬性需要只拷貝那么重復的 get set是一件多么令人討厭的事情。

      解決問題思路,能不能  copy(a, b) 一行代碼搞定!!答案是肯定的反射 a 對象屬性調用 get 方法獲取值,匹配與之對應的 b 對象屬性反射 set 賦值。

      常用 commons-beanutils 是Apache組織下的一個基礎的開源庫,當然確實實現我們的需求但是不盡人意的是該方式性能不太美麗。

      能不能優化下它的性能,聰明的人總會有辦法的從字節碼層面來做拷貝性能就提升,也就是本文的主角 cglib 一款比較底層的操作 java字節碼的框架。

下面通過拷貝bean對象來測試BeanCopier的特性:

public class OrderEntity {    private int id;    private String name;    // Getters and setters are omitted}public class OrderDto {    private int id;    private String name;    // Getters and setters are omitted}public class PropWithDiffType {    private Integer id;    private String name;    // Getters and setters are omitted}public class LackOfSetter {    private int id;    private String name;      public LackOfSetter() {    }      public LackOfSetter(int id, String name) {        this.id = id;        this.name = name;    }    ...}

1. 屬性名稱、類型都相同: 

@Testpublic void normalCopyTest() {    OrderEntity entity = new OrderEntity();    entity.setId(1);    entity.setName("orderName");    final BeanCopier copier = BeanCopier.create(OrderEntity.class, OrderDto.class, false);    OrderDto dto = new OrderDto();    copier.copy(entity, dto, null);    Assert.assertEquals(1, dto.getId());    Assert.assertEquals("orderName", dto.getName());}

結論:拷貝OK。 2. 屬性名稱相同、類型不同:

@Testpublic void sameNameDifferentTypeCopyTest() {    OrderEntity entity = new OrderEntity();    entity.setId(1);    entity.setName("orderName");    final BeanCopier copier = BeanCopier.create(OrderEntity.class, PropWithDiffType.class, false);    PropWithDiffType dto = new PropWithDiffType();    copier.copy(entity, dto, null);    Assert.assertEquals(null, dto.getId()); // OrderEntity的id為int類型,而PropWithDiffType的id為Integer類型,不拷貝    Assert.assertEquals("orderName", dto.getName());}

結論:名稱相同而類型不同的屬性不會被拷貝。 注意:即使源類型是原始類型(int, short和char等),目標類型是其包裝類型(Integer, Short和Character等),或反之:都不會被拷貝。 3. 源類和目標類有相同的屬性(兩者的getter都存在),但目標類的setter不存在

@Testpublic void targetLackOfSetterCopyTest() {    OrderEntity entity = new OrderEntity();    entity.setId(1);    entity.setName("orderName");    final BeanCopier copier = BeanCopier.create(OrderEntity.class, LackOfSetter.class, false);  // 拋NullPointerException    LackOfSetter dto = new LackOfSetter();    copier.copy(entity, dto, null);}

結論:創建BeanCopier的時候拋異常。 導致異常的原因是BeanCopier類的第128~133行

for (int i = 0; i < setters.length; i++) { // 遍歷目標類的屬性描述集    PropertyDescriptor setter = setters[i];    PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName()); // 從源類獲取和目標類屬性名稱相同的屬性描述    if (getter != null) {        MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod()); // 獲取源類屬性的getter方法        MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod()); // 獲取目標類屬性的setter方法。LackOfSetter類name屬性的setter方法沒有,所以報錯

4. 源類或目標類的setter比getter少

@Testpublic void sourceLackOfSetterCopyTest() {    LackOfSetter source = new LackOfSetter(1, "throne");    final BeanCopier copier = BeanCopier.create(LackOfSetter.class, OrderDto.class, false);    OrderDto dto = new OrderDto();    copier.copy(source, dto, null);    Assert.assertEquals(1, dto.getId());    Assert.assertEquals("throne", dto.getName());}

結論:拷貝OK。此時的setter多余,但不會報錯。 總結: 1. BeanCopier只拷貝名稱和類型都相同的屬性。其實,究其原因還是

BeanCopier copier = BeanCopier.create(OrderEntity.class, PropWithDiffType.class, false);
最后一個參數useConverter:false,就表示只拷貝同名同類型的屬性。但是如果使用useConverter,就要自己根據converter中的規則來進行拷貝。

2. 當目標類的setter數目比getter少時,創建BeanCopier會失敗而導致拷貝不成功。

# 至于 BeanCopier要比BeanUtils快多少有人1000萬對象實例拷貝測試如下,雖然是少量數據是毫秒級的差距不明顯,數據量大了也不容小覷。 

測試結果:

SpringCloud 之 Sentinel 限流、熔斷

還在執迷 Docker ?下一代容器 podman 了解下!

Spring Cloud OpenFeign 踩坑日記

免責聲明:本文僅代表文章作者的個人觀點,與本站無關。其原創性、真實性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容文字的真實性、完整性和原創性本站不作任何保證或承諾,請讀者僅作參考,并自行核實相關內容。

http://image99.pinlue.com/thumb/img_jpg/D7le9G2Mc5VwCIx6VibzNaXu218HqS0ACYPKGjXRfxBtGrol86wvm896jfZ75WMGnpfMcD5dErKicJC6ZG1POwVw/0.jpeg
我要收藏
贊一個
踩一下
分享到
?
分享
評論
首頁
竞彩网足球