Bean拷贝工具类性能比较
引言
几年前做过一个项目,接入新的api接口。为了和api实现解耦,决定将api返回的实体类在本地也建一个。这样做有两个好处
- 可以在api变更字段的时候保持应用稳定性
- 可以对返回的实体的属性做处理,以提高可读性。例如接口返回long类型的时间戳,则将该字段在本地实体类中对应字段设置为date类型方便使用。
大致是这样的一个应用场景。当时刚毕业,充斥的都是A.setName(B.getName)这种类型的代码。当字段非常多的时候看起来非常臃肿,最重要的给人感觉不优雅。
再给我一次机会
如果上天再给我一次机会,我会跟他说,我有四种工具类可以优雅的解决这个问题。
分别是- org.apache.commons.beanutils.PropertyUtils.copyProperties
- org.apache.commons.beanutils.BeanUtils.copyProperties
- org.springframework.beans.BeanUtils.copyProperties
- net.sf.cglib.beans.BeanCopier
Talk is cheap, show me code
定义SourceBean
package com.zhaoyangwoo.model;/** * Created by john on 16/7/28. */ public class SourceBean { private Integer age; private String title; private String source; private Boolean eat; public SourceBean(Integer i, String name, Boolean eat, String source) { this.age = i; this.title = name; this.eat = eat; this.source = source; } public void setAge(Integer age) { this.age = age; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Boolean getEat() { return eat; } public void setEat(Boolean eat) { this.eat = eat; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public Integer getAge() { return age; }}
定义DesBean
package com.zhaoyangwoo.model;/** * Created by john on 16/7/28. */ public class DstBean { private Integer age; private String title; private Boolean eat; private String self; public String getSelf() { return self; } public void setSelf(String self) { this.self = self; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Boolean getEat() { return eat; } public void setEat(Boolean eat) { this.eat = eat; }}
测试函数
package com.zhaoyangwoo.biz;import com.zhaoyangwoo.model.DstBean;import com.zhaoyangwoo.model.SourceBean;import net.sf.cglib.beans.BeanCopier;/** * Created by john on 16/7/28. */public class BeanCopyMain {private static BeanCopier beanCopier = BeanCopier.create(SourceBean.class, DstBean.class, false); public static void main(String[] args) throws Exception { Integer[] times = {10000, 1000, 10}; for (int i = 0; i < times.length; i++) { Integer time = times[i]; doCopy(time, (x, y) -> org.apache.commons.beanutils.PropertyUtils.copyProperties(y, x), "org.apache.commons.beanutils.PropertyUtils.copyProperties"); doCopy(time, (x, y) -> org.apache.commons.beanutils.BeanUtils.copyProperties(y, x), "org.apache.commons.beanutils.BeanUtils.copyProperties"); doCopy(time, (x, y) -> org.springframework.beans.BeanUtils.copyProperties(y, x), "org.springframework.beans.BeanUtils.copyProperties"); doCopy(time, (x, y) -> beanCopier.copy(x, y, null), "net.sf.cglib.beans.BeanCopier.copy"); doCopy(time, (x, y) -> { y.setEat(x.getEat()); y.setTitle(x.getTitle()); y.setAge(x.getAge()); }, "getter/setter"); } } static void doCopy(Integer time, BeanCopy beanCopy, String type) throws Exception { long startTime = System.currentTimeMillis(); for (int j = 0; j < time; j++) { SourceBean sourceBean = new SourceBean(j, "source" + j, false, "abc"); DstBean dstBean = new DstBean(); beanCopy.copy(sourceBean, dstBean); } System.out.printf("执行%d次用时%dms---------%s%n", time, System.currentTimeMillis() - startTime, type); } interface BeanCopy { void copy(SourceBean from, DstBean to) throws Exception; }}
执行结果如下
结论
根据上面的运行结果,我们可以得出以下结论
- property少,写起来也不麻烦,就直接用传统的getter/setter,性能最好
- property多,转换不频繁,那就省点事吧,使用org.apache.commons.beanutils.BeanUtils.copyProperties
- property多,转换很频繁,为性能考虑,使用net.sf.cglib.beans.BeanCopier.BeanCopier,性能近乎getter/setter。但是BeanCopier的创建时消耗较大,所以不要频繁创建该实体,最好的处理方式是静态化或者缓存起来。