阅读 687

copy对象,这个操作有点骚!

image.png

这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战

今天不讲设计模式了,一直坚持看我设计模式的人可能知道,设计模式专栏还没有讲完,之所以今天不讲了,是想着最后留着的是最重要的,在我们平常实战过程当中运用最多的,所以我想好好设计一下。争取让每个人看了都怒赞。哈哈 话不多说 今天我们的主角不是设计模式。

(有兴趣的可以在本文末查看,我会把链接发出来)

还是多唠叨几句,欢迎大家关注,点赞,我后面会持续输出干货,大家也可以点击我头像,查看历史干货!

copy对象

为什么要copy对象,大家都知道,在日常开发中,可能A、B、C服务,A服务调用B服务,接口的参数值是大致一样的。过来之后。 DO、DTO、VO等等,这些实体要换转。那么编写映射转换代码是一个繁琐重复且还易错的工作。

(小提示:接口一般用dto去接,返回尽量去vo返回,那么这中间会涉及到转换,比如DTO转POJO)

下面我们看看有几种copy对象的方式,谁才是最“骚”的那一位

BeanUtil类

大家可能最先想到自己实战过程中用的最多的是apache的 BeanUtil类,没错 我刚开始参加工作时,也是用的这个。

org.apache.commons.beanutils.BeanUtils.copyProperties(do, entity);
复制代码

Q:这个有什么问题没?

A: BeanUtil.copyProperties()结合手写get、set,对于简单的转换直接使用BeanUtil,复杂的转换自己手工写get、set。该方案的痛点就在于代码编写效率低、冗余繁杂还略显丑陋,并且BeanUtil因为使用了反射invoke去赋值性能不高。

只能适合bean数量较少、内容不多、转换不频繁的场景。

而且阿里开发规约明确提出(强制)

image.png

所以这里还是要切记使用的。。。

接下来看第二种

Fastjson

对 ,没错 阿里的fastjson也是可以的。

//这种方式,基本上我们都是拿字符串转json使用

StudentsDTO entity = JSON.parseObject(JSON.toJSONString(studentsDO), StudentsDTO.class);
复制代码

这种方案因为通过生成中间json格式字符串,然后再转化成目标对象。

性能可能还没有第一种好,更差一些,同时因为中间会生成json格式字符串,如果转化过多,gc会非常频繁,同时针对复杂场景支持能力不足,基本很少用。

所以第二个也不是最“骚”的那一个。接着往下看!

Spring.BeanUtils

这种方案针对apache的BeanUtils做了很多优化,整体性能提升不少,不过还是使用反射实现比不上原生代码处理,其次针对复杂场景支持能力不足。

下面我截图源码大概看下

image.png

核心就在这个for循环里,它虽然性能有所提升 但依然不是最“骚”的。

Mapstruct

大家可能或多或少见过该框架,一会儿你看完就会发现,我擦 贼“骚”

image.png

我们代码实战演示一波:

maven 引入


//网上有的说,可以引入mapstruct-jdk1.8 我不建议引入,可能会出现未知的问题,一般引入下面两个就行。

<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.0.Final</version>
</dependency>


<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct-processor</artifactId>
   <version>1.4.0.Final</version>
</dependency>
复制代码

注意:这里引入的时候,尽量不要和Sweeager包进行冲突,该包中,也有mapstruct这个东西。大家注意下,如果遇到问题,大家可以下方留言 我们讨论下:

入门实战

比如我们DTO和BO (建议lombok)


@Data 
public class Students {     
    private String name;     
    private int age;     
    private int sex; //0代表男 1:代表女
}
@Data 
public class StudentsDTO {     
    private String name;     
    private int age;     
    private int sex; //0代表男 1:代表女
    
    public StudentsDTO(){}
    
    //构造方法
    public StudentsDTO(String name,int age,int sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
复制代码

定义Mapper,该类是接口类


import org.mapstruct.Mapper;

//所有的骚操作都在这个注解mapper上,一会儿细说原理

@Mapper(componentModel = "spring")
public interface StudentsMapper {   
    
    //dto 转 BO
    Students DTOToStBo(StudentsDTO st); 
}
复制代码

componentModel = "spring" 代表通过生成spring bean注入使用,Mapper注解加上spring配置,会自动生成一个bean,直接使用bean注入即可访问。

@Mapper中描述映射,在编辑的时候mapstruct将会根据此描述生成实现类:

  • 当属性与其目标实体副本同名时,它将被隐式映射。

  • 当目标实体中的属性具有不同名称时,可以通过@Mapping注释指定其名称

对的,当属性名称不一致时,可以用@Mapping注释指定其名称

@Mapper(componentModel = "spring")
public interface StudentsMapper {   
    
    //比如学生类里面,班级属性不一样
    @Mapping(source = "className", target = "grades")
    Students DTOToStBo(StudentsDTO st); 
}
复制代码

怎么调用呢。通过Mappers 工厂生成静态实例使用。(我前面讲过工厂模式和单例模式 大家可以点击头像查看)


import org.mapstruct.factory.Mappers


@Mapper(componentModel = "spring")
public interface StudentsMapper {   
    
    //静态实例化
    StudentsMapper INSTANCE = Mappers.getMapper(StudentsMapper.class);
    
    //比如学生类里面,班级属性不一样
    @Mapping(source = "className", target = "grades")
    Students DTOToStBo(StudentsDTO st); 
}

复制代码

StudentsDTO stDTO = new StudentsDTO("张三",18,1); 
Students students = StudentsMapper.INSTANCE.DTOToStBo(stDTO);

System.out.println("students.name -> "+students.getName());

//输出:张三

复制代码

OK 完成转换赋值,接下来我们大概说说原理

原理

首先说下注解,@Mapper,此注解是当在编译代码时,生成实现类impl,而真正实现对象数据赋值的就是在这个类里面。

当编译好之后 大家可以去target包下面看下:

或者直接这里进去实现类

image.png

image.png

看到没,原来核心是这里,贼“骚”。

上面我们用gatMapper 其原理是就是为了拿这个实现类

image.png

OK 今天就讲到这里。

弦外之音

感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。也欢迎有问题我们下面评论交流

加油! 我们下期再见! 下面是本月精讲的设计模式 欢迎阅读:

设计模式之单例模式

设计模式之工厂模式

设计模式之建造者模式

设计模式之代理模式

设计模式之访问者模式

设计模式之适配器模式

设计模式之命令者模式

java状态模式 | 随时随地监控状态改变

java观察者模式 | 如何通知事物的变化

java备忘录模式 | 如何记录历史信息

java迭代器模式模式 | 如何对各个元素进行访问

java享元模式 | 如何共享对象

java解释器模式 | 自定义规则实现逻辑

java桥接模式 | 抽象与不同实现的绑定

文章分类
后端
文章标签