七种对象复制工具类,阿粉该 Pick 谁?(二)

简介: 日常编程中,我们会经常会碰到对象属性复制的场景,就比如下面这样一个常见的三层 MVC 架构。

Spring BeanUtils

Spring 属性复制工具类类名与 Apache 一样,基本用法也差不多。我先来看下 Spring BeanUtils 基本用法。

同样,我们先引入依赖,从名字我们可以看出,BeanUtils 位于 Spring-Beans 模块,这里我们依然使用最新模块。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

这里我们使用 DTO 与 DO 复用上面的例子,转换代码如下:

// 省略上面赋值代码,与上面一致
StudentDO studentDO = new StudentDO();
BeanUtils.copyProperties(studentDTO, studentDO);

从用法可以看到,Spring BeanUtils 与 Apache 有一个最大的不同,两者源对象与目标对象参数位置不一样,阿粉之前没注意,用了 Spring 工具类,但是却是按照 Apache 的用法使用。

此时对比studentDOstudentDTO对象:

32.jpg

从上面的对比图我们可以得到一些结论:

  • 字段名不一致,属性无法复制
  • 类型不一致,属性无法复制。但是注意,如果类型为基本类型以及基本类型的包装类,这种可以转化
  • 嵌套对象字段,将会与源对象使用同一对象,即使用浅拷贝

除了这个方法之外,Spring BeanUtils 还提供了一个重载方法:

public static void copyProperties(Object source, Object target, String... ignoreProperties)

使用这个方法,我们可以忽略某些不想被复制过去的属性:

BeanUtils.copyProperties(studentDTO, studentDO,"name");

这样,name 属性就不会被复制到 DO 对象中。

33.jpg

虽然 Spring BeanUtils 与 Apache BeanUtils 功能差不多,但是在性能上 Spring BeanUtils  还是完爆 Apache BeanUtils。主要原因还是在于 Spring 并没有与 Apache 一样使用反射做了过多校验,另外 Spring BeanUtils 内部使用了缓存,加快转换的速度。

所以两者选择,还是推荐使用 Spring BeanUtils。

Cglib BeanCopier

上面两个是阿粉日常工作经常使用,而下面的这些都是阿粉最近才开始接触的,比如 Cglib BeanCopier。这个使用方法,可能比上面两个类稍微复杂一点,下面我们来看下具体用法:

首先我们引入 Cglib 依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

画外音:如果你工程内还有 Spring-Core 的话,如果查找 BeanCopier 这个类,可以发现两个不同的包的同名类。

一个属于 Cglib,另一个属于 Spring-Core。

其实 Spring-Core 内BeanCopier 实际就是引入了 Cglib 中的类,这么做的目的是为包了保证 Spring 使用长度 Cglib 相关类的稳定性,防止外部 Cglib 依赖不一致,导致 Spring 运行异常。

转换代码如下:

// 省略赋值语句
StudentDO studentDO = new StudentDO();
BeanCopier beanCopier = BeanCopier.create(StudentDTO.class, StudentDO.class, false);
beanCopier.copy(studentDTO, studentDO, null);

使用方法相比 BeanUtils, BeanCopier 稍微多了一步。 对比studentDOstudentDTO对象:

34.jpg

从上面可以得到与 Spring Beanutils 基本一致的结论:

  • 字段名不一致,属性无法复制
  • 类型不一致,属性无法复制。不过有点不一样,如果类型为基本类型/基本类型的包装类型,这两者无法被拷贝。
  • 嵌套对象字段,将会与源对象使用同一对象,即使用浅拷贝

上面我们使用 Beanutils,遇到这种字段名,类型不一致的这种情况,我们没有什么好办法,只能手写硬编码。

不过在 BeanCopier 下,我们可以引入转换器,进行类型转换。

// 注意最后一个属性设置为 true
BeanCopier beanCopier = BeanCopier.create(StudentDTO.class, StudentDO.class, true);
// 自定义转换器
beanCopier.copy(studentDTO, studentDO, new Converter() {
    @Override
    public Object convert(Object source, Class target, Object context) {
        if (source instanceof Integer) {
            Integer num = (Integer) source;
            return num.toString();
        }
        return null;
    }
});

不过吐槽一下这个转换器,一旦我们自己打开使用转换器,所有属性复制都需要我们自己来了。比如上面的例子中,我们只处理当源对象字段类型为 Integer,这种情况,其他都没处理。我们得到 DO 对象将会只有 name 属性才能被复制。

35.jpg

Cglib BeanCopier 的原理与上面两个 Beanutils 原理不太一样,其主要使用 字节码技术动态生成一个代理类,代理类实现get 和 set方法。生成代理类过程存在一定开销,但是一旦生成,我们可以缓存起来重复使用,所有 Cglib 性能相比以上两种 Beanutils 性能比较好。

相关文章
|
7月前
|
前端开发 编译器
一个无用工具类
一个无用工具类
59 2
|
8月前
|
算法 Java
【面试题精讲】JVM-详细说说引用计数法的缺点-循环依赖
【面试题精讲】JVM-详细说说引用计数法的缺点-循环依赖
|
8月前
|
存储 缓存 Java
【面试题精讲】Java包装类缓存机制
【面试题精讲】Java包装类缓存机制
|
24天前
|
存储 算法 Java
[Java 源码] 秋招常被问到 GC 相关的几道面试题(集中在分配以及回收)
[Java 源码] 秋招常被问到 GC 相关的几道面试题(集中在分配以及回收)
|
9月前
|
存储 机器学习/深度学习 缓存
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
|
Java Python Spring
spring注入静态变量有几种方法?不看的都掉坑里了!
spring注入静态变量有几种方法?不看的都掉坑里了!
|
Java 数据库
ThreadLocal的使用和坑点
ThreadLocal的使用和坑点
167 0
|
缓存 Java
Java包装类缓存机制
首先,来看一道常见的面试题,下面代码运行后会输出什么?
75 0
|
缓存 Java
弱引用该怎么用?
弱引用该怎么用?
96 0
弱引用该怎么用?
|
存储 缓存 Java
【JDK源码】Java包装类的缓存是怎么回事
包装类是对Java中基本类型的封装,在 JDK5 中引入了包装类的缓存机制,有助于节省内存……
255 0

热门文章

最新文章