【轉載】主流對象複製框架使用與比較

在 Java 生態中,有不少第三方框架能夠進行對象複製,分別是Apache的BeanUtils和PropertyUtils、Spring的BeanUtils、Cglib的BeanCopier等,下面咱們一塊兒來了解一下吧!java

1、摘要

對象複製,在實際開發中也是常常須要使用到的,想必最傻瓜式的就是建立一個對象,而後一個一個屬性進行set/get,可是若是面對一個對象有100多個屬性的狀況,顯然這種方法效率底、並且容易出錯!spring

固然也有辦法,使用 Java 提供的克隆方法,也能夠實現對象拷貝,可是 Java 提供的對象拷貝,只能進行淺克隆,若是要進行深度克隆,可使用序列化流實現對象深度複製。apache

雖然上述方法,能夠解決對象的複製,假如咱們有兩個類,屬性都基本同樣,如今的你想要實現屬性參數值複製,應該怎麼解決呢?數據結構

其實解決思路很簡單,就是找到兩個類的名稱、類型是否一致,若是一致,就將要複製的對象內容拷貝到另外一個對象的屬性上。框架

固然,在 Java 生態中已經有不少成熟的第三方框架實現了這一功能,在這裏,介紹一下 java 的 Bean 複製框架,分別有ApacheBeanUtilsPropertyUtilsSpringBeanUtilsCglibBeanCopier等。ide

也很少說了,直接擼代碼!工具

2、方法介紹

下面咱們來建立兩個類FromBeanToBean,兩個類除了名稱不同,屬性都同樣,以下:性能

public class FromBean {    
    private String id;    
    private String name;    
    private Integer age;    
    private Date createTime;    
    private BigDecimal money;    
    //... 省略 setter 和 getter
}
public class ToBean {    
    private String id;    
    private String name;    
    private Integer age;    
    private Date createTime;    
    private BigDecimal money;    
    //... 省略 setter 和 getter
}

爲了後面更好的比較各個框架的性能,這裏使用策略模式,來實現各個方法,先新建一個對象複製接口,以下:測試

public interface BeanCopyService {    
    /**     
    * 使用的工具包名稱    
    * @return     
    */    
    String methodName();    
    /**     
    * 執行復制     
    * @param fromBean     
    * @return     
    */    
    ToBean copyProperty(FromBean fromBean) throws Exception;
}

新建一個對象複製策略類,以下:this

public class BeanCopyStragegy {    
    /**     
    * 進行復制     
    * @param beanCopyService     
    * @param fromBean     
    */    
    public void execute(BeanCopyService beanCopyService, FromBean fromBean) throws Exception {        
        System.out.println("=========================");        
        System.out.println("使用的工具包:" + beanCopyService.methodName());        
        ToBean toBean = beanCopyService.copyProperty(fromBean);         
        System.out.println("複製後的對象:" + JSON.toJSONString(toBean));    
    }
}

最後,建立一個測試類,以下:

public class BeanCopyClient {    
    public static void main(String[] args) throws Exception {        
        //初始化一個源對象bean        
        FromBean fromBean = new FromBean();        
        fromBean.setId("ID0001");        
        fromBean.setName("張三");        
        fromBean.setAge(18);        
        fromBean.setCreateTime(new Date());        
        fromBean.setMoney(new BigDecimal(100));        
        //apache的BeanUtils        
        BeanCopyService apacheBeanUtils = new BeanCopyService() {            
            @Override            
            public String methodName() {                
                return "apache BeanUtils";            
            }            
            @Override            
            public ToBean copyProperty(FromBean fromBean) throws Exception {                
                ToBean toBean = new ToBean();                
                org.apache.commons.beanutils.BeanUtils.copyProperties(toBean,fromBean);                
                return toBean;            
            }        
        };        
        //apache的PropertyUtils        
        BeanCopyService apachePropertyUtils = new BeanCopyService() {            
            @Override            
            public String methodName() {                
                return "apache PropertyUtils";            
            }            
            @Override            
            public ToBean copyProperty(FromBean fromBean) throws Exception {                
                ToBean toBean = new ToBean();                
                org.apache.commons.beanutils.PropertyUtils.copyProperties(toBean,fromBean);                
                return toBean;            
            }        
        };        
        //spring的BeanUtils        
        BeanCopyService springBeanUtils = new BeanCopyService() {            
            @Override            
            public String methodName() {                
                return "spring BeanUtils";            
            }            
            @Override            
            public ToBean copyProperty(FromBean fromBean) throws Exception {                
                ToBean toBean = new ToBean();                
                org.springframework.beans.BeanUtils.copyProperties(fromBean, toBean);                
                return toBean;            
            }        
        };        
        //cglib的BeanCopier        
        BeanCopyService cglibBeanCopier = new BeanCopyService() {            
            @Override            
            public String methodName() {                
                return "cglib BeanCopier";            
            }            
            @Override            
            public ToBean copyProperty(FromBean fromBean) throws Exception {                
                ToBean toBean = new ToBean();                
                net.sf.cglib.beans.BeanCopier.create(FromBean.class, ToBean.class, false).copy(fromBean, toBean, null);                
                return toBean;            
            }        
        };        
        System.out.println("源對象的內容:" + JSON.toJSONString(fromBean));       
        //進行測試       
        BeanCopyStragegy stragegy = new BeanCopyStragegy();       
        stragegy.execute(apacheBeanUtils, fromBean);//apache的BeanUtils       
        stragegy.execute(apachePropertyUtils, fromBean);//apache的PropertyUtils       
        stragegy.execute(springBeanUtils, fromBean);//spring的BeanUtils       
        stragegy.execute(cglibBeanCopier, fromBean);//cglib的BeanCopier    
}}

運行程序,輸出結果以下:

其中 apache 提供了2個類來實現對象複製,分別是BeanUtilsPropertyUtils,這二者的區別在於BeanUtils提供類型轉換功能,即發現兩個JavaBean的同名屬性爲不一樣類型時,在支持的數據類型範圍內進行轉換,而PropertyUtils不支持這個功能!

關於這點,咱們將ToBean類中的money類型修改爲String,以下:

public class ToBean {    
    private String id;    
    private String name;    
    private Integer age;    
    private Date createTime;    
    private String money;//修改money的類型爲String    
    //... 省略 setter 和 getter
}

在運行程序,輸入結果以下:

從結果能夠看出,apacheBeanUtils依然能夠完整複製,可是PropertyUtils報錯!

springBeanUtilscglibBeanCopier,由於屬性對應的類型不一致,直接跳過複製!

既然,四者均可以複製,咱們從性能角度來觀察一下各個框架複製的效率以下,咱們將BeanCopyStragegy類進行改造,以下:

public class BeanCopyStragegy {    
    private Integer count;    
    public BeanCopyStragegy(Integer count) {        
        this.count = count;    
    }    
    /**     
    * 進行復制     
    * @param beanCopyService     
    * @param fromBean     
    */   
    public void execute(BeanCopyService beanCopyService, FromBean fromBean) throws Exception {
        System.out.print("使用的工具包:" + beanCopyService.methodName());        
        long start = System.currentTimeMillis();        
        for (int i = 0; i < count; i++) {            
            ToBean toBean = beanCopyService.copyProperty(fromBean);        
        }        
        System.out.print("循環複製對象"+count+"次,耗時:" + (System.currentTimeMillis() - start) +"\n");    
    }
}

在測試的時候,傳入循環次數!

//傳入循環次數,進行測試
BeanCopyStragegy stragegy = new BeanCopyStragegy(10);

咱們分別傳入10100100010000100000,最終各個框架耗時結果以下:

可能每臺機器測試結果有差別,從結果能夠得出以下結論:

  • 在低頻次下,apache的PropertyUtils效率最高,在高頻次下,cglib BeanCopier效率最高;
  • apache的BeanUtils,在四個框架中效率最低;
  • 在高頻次下,spring的BeanUtils的效率僅次於cglib BeanCopier;

3、總結

由於apacheBeanUtils在進行復制的時候,在支持的數據類型範圍內進行轉換,全部性能會有些損失,對於數據結構嚴謹的複製,不建議採用!

相關文章
相關標籤/搜索