栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

几种Bean映射工具介绍

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

几种Bean映射工具介绍

文章目录

一、介绍

1、功能介绍2、不同方法与性能对比 二、转换案例

1、源VO和目标VO2、get/set3、Json2Json4、Spring copyProperties✨5、BeanCopier6、MapStruct✨✨

一、介绍 1、功能介绍

在 Java 系统工程开发过程中,都会有各个层之间的对象转换,比如 VO、DTO、PO、VO 等,而如果都是手动get、set又太浪费时间,还可能操作错误,所以选择一个自动化工具会更加方便

2、不同方法与性能对比

目前用于对象属性转换有12种,包括普通的get/set、json2Json、Apache属性拷贝、Spring属性拷贝、bean-mapping、bean-mapping-asm、BeanCopier、Orika、Dozer、ModelMapper、JMapper、MapStruct

Spring 提供的BeanUtils.copyProperties 是大家代码里最常出现的工具类,注意不是 Apache 包下手动get、set性能是最好的,另外 MapStruct 性能和操作也很方便,因为它本身就是在编译期生成get、set代码,和我们写get、set一样其他一些组件包主要基于 AOP、ASM、CGlib,的技术手段实现的,所以也会有相应的性能损耗
二、转换案例 1、源VO和目标VO

//源类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class SourceVO {
    private String name;
    private String password;
    private Integer age;
}

//目标类
@Data
@Accessors(chain = true)
public class TargetVO {
    private String name;
    private String password;
    private Integer age;
}

2、get/set

这种方式也是日常使用的最多的,性能十分优秀,但是操作起来有点麻烦。多个get/set可以通过 Shift+Alt 选中所有属性,Shift+Tab 归并到一列,接下来在使用 Alt 选中这一列,批量操作粘贴 targetVO.set 以及快捷键大写属性首字母,最后切换到结尾补充括号和分号,最终格式化一下就可以了

3、Json2Json

把对象转JSON串,再把JSON转另外一个对象,这种方式性能不是很高

public class ObjectConversion {

    
    public static  List copy(List list,Class clazz){
        String oldOb = JSON.toJSONString(list);
        return JSON.parseArray(oldOb, clazz);
    }

    
    public static  T copy(Object ob, Class clazz){
        String oldOb = JSON.toJSONString(ob);
        return JSON.parseObject(oldOb, clazz);
    }
}
public class Test {
    public static void main(String[] args){
        SourceVO sourceVO = new SourceVO("shawn","123456",18);
        TargetVO targetVO = ObjectConversion.copy(sourceVO, TargetVO.class);
        System.out.println(targetVO);
    }
}
4、Spring copyProperties✨

这个方法是反射的属性拷贝,Spring 提供的 copyProperties 要比 Apache 好用的多,性能和操作都比较好,这个包是org.springframework.beans.BeanUtils

另外这里考虑到属性不同的字段的拷贝,还额外创建了一个回调函数进行映射处理

//回调函数定义,这里用了java8特性函数式接口
@FunctionalInterface
public interface BeanCopyUtilCallBack  {

    
    void callBack(S t, T s);
}
public class BeanCopyUtil extends BeanUtils {

    
    public static  List copyListProperties(List sources, Supplier target) {
        return copyListProperties(sources, target, null);
    }


    
    public static  List copyListProperties(List sources, Supplier target, BeanCopyUtilCallBack callBack) {
        List list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            copyProperties(source, t);
            list.add(t);
            if (callBack != null) {
                // 回调
                callBack.callBack(source, t);
            }
        }
        return list;
    }
}

这里的回调函数可以使用Enum枚举进行规范化,最终进行测试,成功拷贝

ublic class Test {
    public static void main(String[] args){
        // 单个拷贝
        SourceVO sourceBean = new SourceVO("shawn", "123456", 18);
        TargetVO targetBean = new TargetVO();
        BeanUtils.copyProperties(sourceBean,targetBean);
        System.out.println(targetBean);

        // 列表拷贝
        List sourceVOList = new ArrayList<>();
        sourceVOList.add(new SourceVO("shawn","123456",18));
        sourceVOList.add(new SourceVO("shawn1","12345678",20));
        List targetVOList= BeanCopyUtil.copyListProperties(sourceVOList, TargetVO::new,
                (sourceVO,targetVO)->{
            targetVO.setAge(sourceVO.getAge());
        });
        System.out.println(targetVOList);
    }
}
5、BeanCopier

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

//工具类
public class WrapperBeanCopier {

    
    public static  T copyProperties(M source, Class clazz){
        if (Objects.isNull(source) || Objects.isNull(clazz))
            throw new IllegalArgumentException();
        return copyProperties(source, clazz, null);
    }

    
    public static  List copyObjects(List sources, Class clazz) {
        if (Objects.isNull(sources) || Objects.isNull(clazz) || sources.isEmpty())
            throw new IllegalArgumentException();
        BeanCopier copier = BeanCopier.create(sources.get(0).getClass(), clazz, false);
        return Optional.of(sources)
                .orElse(new ArrayList<>())
                .stream().map(m -> copyProperties(m, clazz, copier))
                .collect(Collectors.toList());
    }

    
    private static  T copyProperties(M source, Class clazz, BeanCopier copier){
        if (null == copier){
            copier = BeanCopier.create(source.getClass(), clazz, false);
        }
        T t = null;
        try {
            t = clazz.newInstance();
            copier.copy(source, t, null);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }
}
public class Test {
    public static void main(String[] args){
        // 单个拷贝
        SourceVO sourceVO = new SourceVO("shawn", "123456", 18);
        TargetVO targetVO = WrapperBeanCopier.copyProperties(sourceVO, TargetVO.class);
        System.out.println(targetVO);

        // 列表拷贝
        List sourceVOList = new ArrayList<>();
        sourceVOList.add(new SourceVO("shawn","123456",18));
        sourceVOList.add(new SourceVO("shawn1","12345678",20));
        List targetVOS = WrapperBeanCopier.copyObjects(sourceVOList, TargetVO.class);
        System.out.println(targetVOS);
    }
}

如果要自定义转换,可以使用new Converter(),一旦我们自己打开使用转换器,所有属性复制都需要我们自己来了,否则会导致无法复制。另外这里需要注意的是拷贝对象要去除@Accessors(chain = true)注解,因为该注解会将 setter 方法的返回值由 void 修改为当前对象。这导致 setter 的方法签名改变,最终导致 BeanCopier 无法识别现有的 setter 方法

6、MapStruct✨✨

官网文档地址:https://github.com/mapstruct/mapstruct

MapStruct是一款基于Java注解的对象属性映射工具,在Github上已经有4.5K+Star。使用的时候我们只要在接口中定义好对象属性映射规则,它就能自动生成映射实现类,不使用反射,性能优秀,能实现各种复杂映射。(强烈推荐)

首先导入Maven依赖


    org.mapstruct
    mapstruct
    1.4.2.Final


    org.mapstruct
    mapstruct-processor
    1.4.2.Final
    compile

创建Bean对象

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class SourceVO {
    private String name;
    private String password;
    private Integer age;
    private Date birthday;
}


@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class TargetVO {
    private String name;
    private String pwd;
    private Integer age;
    private String birthday;
}

编写Mapper文件,实现同名同类型属性、不同名称属性、不同类型属性的映射

@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(target = "pwd", source = "password")
    @Mapping(source = "birthday",target = "birthday", dateFormat = "yyyy-MM-dd")
    TargetVO getTargetVO(SourceVO sourceVO);
}

最后直接通过接口中的INSTANCE实例调用转换方法

public class Test {
    public static void main(String[] args){
        SourceVO sourceVO = new SourceVO("shawn", "123456", 18, new Date());
        TargetVO targetVO = UserMapper.INSTANCE.getTargetVO(sourceVO);
        System.out.println(targetVO);
    }
}

其实MapStruct的实现原理很简单,就是根据我们在Mapper接口中使用的@Mapper和@Mapping等注解,在运行时生成接口的实现类,我们可以打开项目的target目录查看

初次之外,MapStruct使用有:

基本映射、集合映射、子对象映射、合并映射使用依赖注入、使用常量/默认值/表达式、映射前后自定义、处理映射异常等

详情可参考:
https://blog.csdn.net/zhenghongcs/article/details/121349361
https://mapstruct.org/documentation/stable/reference/html/


参考文章

https://mp.weixin.qq.com/s/_QJa5RSxvPBsqXo8yS5-pg

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号