日常编程中,我们经常会碰到对象属性复制的场景,当类的属性数量只有简单的几个时,我们通过手写set/get即可完成,但是属性有十几个,甚至几十个的时候,通过set/get的方式,可能会占用大量的编程时间,关键是像这样的代码,基本上是机械式的操作。
面对这种重复又枯燥的编程工作,很多的行业大佬,开发出了通用的对象属性复制工具,以免去机械式的编程。
小编经过实际的调研,发现目前开源市场上,用得比较多的对象属性复制工具有以下几个:
下面我们一起来看看,他们的使用方式以及性能对比,最后根据不同的使用需求,总结出如何选择。
首先我们定义一个UserInfo类,下面我们会以此类作为对象属性复制的案例,内容如下:
public class UserInfo {
/**
* 用户ID
*/
private Long userId;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String userPwd;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private String gender;
/**
* 生日
*/
private Date birthday;
// ...set、get
@Override
public String toString() {
return "UserInfo{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userPwd='" + userPwd + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", birthday=" + birthday +
'}';
}
}
Apache BeanUtils 这个工具,相信很多人都接触过,因为它在 Java 领域属性复制这块非常有名,早期使用的非常广泛!
使用方式上也非常的简单,首先在项目中导入commons-beanutils包。
commons-beanutils
commons-beanutils
1.9.4
然后在代码中直接导入org.apache.commons.beanutils.BeanUtils工具进行对象属性复制,样例代码如下:
// 原始对象
UserInfo source = new UserInfo();
// set...
// 目标对象
UserInfo target = new UserInfo();
BeanUtils.copyProperties(target, source);
System.out.println(target.toString());
Apache BeanUtils 工具从操作使用上还是非常方便的,不过其底层源码为了追求完美,加了过多的包装,使用了很多反射,做了很多校验,导致属性复制时性能较差,因此阿里巴巴开发手册上强制规定避免使用 Apache BeanUtils。
关于对象属性复制性能,我们会文末向大家介绍!
Spring 对象属性复制工具类,类名与 Apache 一样,基本用法也差不多。
首先在项目中导入spring-beans包。
org.springframework
spring-beans
4.3.30.RELEASE
同样的,在代码中直接导入org.springframework.beans.BeanUtils工具进行对象属性复制,样例代码如下:
// 原始对象
UserInfo source = new UserInfo();
// set...
// 目标对象
UserInfo target = new UserInfo();
BeanUtils.copyProperties(source, target);
System.out.println(target.toString());
除此之外,Spring BeanUtils 还提供了重载方法。
public static void copyProperties(Object source, Object target, String... ignoreProperties);
如果我们不想对象中的某些属性被复制过去,可以通过如下方式实现。
BeanUtils.copyProperties(source, target, "userPwd");
虽然Apache BeanUtils和Spring BeanUtils使用起来都很方便,但是两者性能差异非常大,Spring BeanUtils的对象属性复制速度比Apache BeanUtils要快很多,主要原因在于 Spring 并没有像 Apache 一样使用反射做过多的参数校验,另外Spring BeanUtils内部使用了缓存,加快了转换的速度。
还有一个需要注意的地方是,Apache BeanUtils和Spring BeanUtils的类名和方法基本上相同,但是它们的原始对象和目标对象的参数位置是相反的,如果直接从Apache BeanUtils切换到Spring BeanUtils有巨大的风险,如果有这个方面的需要,请一个一个的替换!
Cglib BeanCopier 对象属性复制工具类,相比Spring BeanUtils和Apache BeanUtils,用法稍微要多一步代码。
首先在项目中导入cglib包。
cglib
cglib
3.3.0
然后在代码中直接导入net.sf.cglib.beans.BeanCopier工具进行对象属性复制,样例代码如下:
// 原始对象
UserInfo source = new UserInfo();
// set...
// 获取一个复制工具
BeanCopier beanCopier = BeanCopier.create(UserInfo.class, UserInfo.class, false);
// 对象属性值复制
UserInfo target = new UserInfo();
beanCopier.copy(source, target, null);
System.out.println(target.toString());
如果遇到字段名相同,但是类型不一致的对象复制,可以引入转换器,进行类型转换,比如这样:
UserInfo source = new UserInfo();
// set...
// 创建一个复制工具
BeanCopier beanCopier = BeanCopier.create(UserInfo.class, UserInfo.class, true);
// 自定义对象属性值复制
UserInfo target = new UserInfo();
beanCopier.copy(source, target, new Converter() {
@Override
public Object convert(Object source, Class target, Object context) {
if(source instanceof Integer){
return String.valueOf(source);
}
return source;
}
});
System.out.println(target.toString());
Cglib BeanCopier 的工作原理与上面两个Beanutils原理不太一样,其主要使用字节码技术动态生成一个代理类,通过代理类来实现get/set方法。
虽然生成代理类过程存在一定开销,但是一旦生成可以重复使用,因此 Cglib 性能相比以上两种 Beanutils 性能都要好。
另外就是,如果你的工程是基于 Spring 框架开发的,查找 BeanCopier 这个类的时候,可以发现两个不同的包,一个属于Cglib,另一个属于Spring-Core。
其实Spring-Core内置的BeanCopier引入了 Cglib 中的类,这么做的目的是为保证 Spring 中使用 Cglib 相关类的稳定性,防止外部 Cglib 依赖不一致,导致 Spring 运行异常,因此无论你引用那个包,本质都是使用 Cglib。
MapStruct 也是一款对象属性复制的工具,但是它跟我们上面介绍的几款工具技术实现思路都不一样,主要区别在于:无论是Beanutils还是BeanCopier,都是程序运行期间去执行对象属性复制操作;而MapStruct是在程序编译期间,就已经生成好了对象属性复制相关的逻辑。
因此可以想象的到,MapStruct的复制性能要快很多!
MapStruct工具的使用也很简单,首先导入相关包。
org.mapstruct
mapstruct
1.5.0.Final
org.mapstruct
mapstruct-processor
1.5.0.Final
provided
然后定义一个对象属性拷贝接口。
@Mapper
public interface UserInfoMapper {
UserInfoMapper INSTANCE = Mappers.getMapper(UserInfoMapper.class);
UserInfo copy(UserInfo source);
}
最后,在代码中调用即可。
// 原始对象
UserInfo source = new UserInfo();
// set...
// 对象属性复制
UserInfo target = UserInfoMapper.INSTANCE.copy(source);
System.out.println(target.toString());
MapStruct实现原理,刚刚也介绍了,是在编译期间生成对象属性复制相关的代码逻辑,以上面的操作为例,打开class目录文件,你会看到一个UserInfoMapperImpl实现类,已经帮我们做好了set/get操作,源码内容如下:
public class UserInfoMapperImpl implements UserInfoMapper {
public UserInfoMapperImpl() {
}
public UserInfo copy(UserInfo source) {
if (source == null) {
return null;
} else {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(source.getUserId());
userInfo.setUserName(source.getUserName());
userInfo.setUserPwd(source.getUserPwd());
userInfo.setAge(source.getAge());
userInfo.setGender(source.getGender());
userInfo.setBirthday(source.getBirthday());
return userInfo;
}
}
}
了解完以上的对象属性复制工具之后,回到我们最初发出的疑问,到底谁最强呢?
下面我们通过循环执行对象属性复制次数,分别测试set/get、Apache BeanUtils、Spring BeanUtils、Cglib BeanCopier、MapStruct等执行方法,看看它们所消耗的时间如何,统计报表明细如下!
从图中我们可以得出如下结论!
本文主要围绕对象属性复制,从使用方面做了一次简单的内容总结。
通过以上 5 种方式的对象属性复制操作,给出的建议如下:
最后,以上的对象属性复制工具都是浅拷贝的实现方式,如果要深拷贝,可以使用对象序列户和反序列化技术实现!
当前文章:对象属性拷贝,到底谁更强?
网站URL:http://www.shufengxianlan.com/qtweb/news49/317549.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联