在 Java 生態中,有不少第三方框架能夠進行對象複製,分別是Apache的BeanUtils和PropertyUtils、Spring的BeanUtils、Cglib的BeanCopier等,下面咱們一塊兒來了解一下吧!java
對象複製,在實際開發中也是常常須要使用到的,想必最傻瓜式的就是建立一個對象,而後一個一個屬性進行set/get,可是若是面對一個對象有100多個屬性的狀況,顯然這種方法效率底、並且容易出錯!spring
固然也有辦法,使用 Java 提供的克隆方法,也能夠實現對象拷貝,可是 Java 提供的對象拷貝,只能進行淺克隆,若是要進行深度克隆,可使用序列化流實現對象深度複製。apache
雖然上述方法,能夠解決對象的複製,假如咱們有兩個類,屬性都基本同樣,如今的你想要實現屬性參數值複製,應該怎麼解決呢?數據結構
其實解決思路很簡單,就是找到兩個類的名稱、類型是否一致,若是一致,就將要複製的對象內容拷貝到另外一個對象的屬性上。框架
固然,在 Java 生態中已經有不少成熟的第三方框架實現了這一功能,在這裏,介紹一下 java 的 Bean 複製框架,分別有Apache
的BeanUtils
和PropertyUtils
、Spring
的BeanUtils
、Cglib
的BeanCopier
等。ide
也很少說了,直接擼代碼!工具
下面咱們來建立兩個類FromBean
、ToBean
,兩個類除了名稱不同,屬性都同樣,以下:性能
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個類來實現對象複製,分別是BeanUtils
和PropertyUtils
,這二者的區別在於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 }
在運行程序,輸入結果以下:
從結果能夠看出,apache
的BeanUtils
依然能夠完整複製,可是PropertyUtils
報錯!
而spring
的BeanUtils
和cglib
的BeanCopier
,由於屬性對應的類型不一致,直接跳過複製!
既然,四者均可以複製,咱們從性能角度來觀察一下各個框架複製的效率以下,咱們將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);
咱們分別傳入10
、100
、1000
、10000
、100000
,最終各個框架耗時結果以下:
可能每臺機器測試結果有差別,從結果能夠得出以下結論:
由於apache
的BeanUtils
在進行復制的時候,在支持的數據類型範圍內進行轉換,全部性能會有些損失,對於數據結構嚴謹的複製,不建議採用!