范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

BeanCopierBeanUtils对象属性拷贝

  在做业务的时候,为了隔离变化,我们会将DAO查询出来的DO和对前端提供的DTO隔离开来,它们的结构都是类似的。写很多冗长的b.setFiled(a.getFiled())这样的代码,是繁琐无意义的。于是需要简化对象拷贝方式。大多时候使用的是Apache或Spring的BeanUtils,还有另一个更高效的属性拷贝方式:BeanCopier。一、背景
  1 对象拷贝概念
  Java中,数据类型分为值类型(基本数据类型)和引用类型。对象拷贝分为浅拷贝(浅克隆)与深拷贝(深克隆)。
  2 示例前准备。源对象属性类UserDO.class(以下示例,源对象都用这个)@Data public class UserDO {       private int id;       private String userName;       private LocalDateTime gmtBroth;       private BigDecimal balance;       public UserDO(Integer id, String userName, LocalDateTime gmtBroth, BigDecimal balance) {             this.id = id;             this.userName = userName;             this.gmtBroth = gmtBroth;             this.balance = balance;       } }
  造数据工具类DataUtilpublic class DataUtil {       /**          * 模拟查询出一条数据            * @return            */       public static UserDO createData() {             return new UserDO(1, "Van", LocalDateTime.now(),new BigDecimal(100L));       }       /**              * 模拟查询出多条数据            * @param num 数量            * @return            */       public static List createDataList(int num) {             List userDOS = new ArrayList<>();             for (int i = 0; i < num; i++) {                 UserDO userDO = new UserDO(i+1, "Van", LocalDateTime.now(),new BigDecimal(100L));                 userDOS.add(userDO);           }             return userDOS;      } }二、对象拷贝之BeanUtils
  Apache和Spring均有BeanUtils工具类, Apache的BeanUtils稳定性与效率都不行;Spring的BeanUtils比较稳定,不会因为量大了,耗时明显增加,故一般都使用Spring的BeanUtils。
  1 源码解读
  Spring中的BeanUtils,其中实现的方式很简单,就是对两个对象中相同名字的属性进行简单get/set,仅检查属性的可访问性。成员变量赋值是基于目标对象的成员列表,并且会跳过ignore的以及在源对象中不存在的,所以这个方法是安全的,不会因为两个对象之间的结构差异导致错误,但是必须保证同名的两个成员变量类型相同。
  2 示例@Slf4j public class BeanUtilsDemo {       public static void main(String[] args) {             long start = System.currentTimeMillis();             UserDO userDO = DataUtil.createData();             log.info("拷贝前,userDO:{}", userDO);             UserDTO userDTO = new UserDTO();     BeanUtils.copyProperties(userDO,userDTO);             log.info("拷贝后,userDO:{}", userDO);   long end = System.currentTimeMillis();      } }
  结果18:12:11.734 [main] INFO cn.van.parameter.bean.copy.demo.BeanUtilsDemo  - 拷贝前,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T18:12:11.730, balance=100) 18:12:11.917 [main] INFO cn.van.parameter.bean.copy.demo.BeanUtilsDemo  - 拷贝后,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T18:12:11.730, balance=100)三、对象拷贝之BeanCopier
  BeanCopier是用于在两个bean之间进行属性拷贝的。BeanCopier支持两种方式:
  1 一种是不使用Converter的方式,仅对两个bean间属性名和类型完全相同的变量进行拷贝;
  2 另一种则引入Converter,可以对某些特定属性值进行特殊操作。基本使用依赖     cglib     cglib-nodep     3.3.0 
  注意:该依赖非必须,因为Spring中已经集成了cglib,本文使用的就是org.springframework.cglib.beans.BeanCopier。3.1.1 属性名称、类型都相同目标对象属性类@Data public class UserDTO {       private int id;       private String userName; }测试方法/** * 属性名称、类型都相同(部分属性不拷贝) */ private static void normalCopy() {   // 模拟查询出数据     UserDO userDO = DataUtil.createData();     log.info("拷贝前,userDO:{}", userDO);     //第一个参数:源对象。 //第二个参数:目标对象。 //第三个参数:是否使用自定义转换器(下面会介绍),下同     BeanCopier b = BeanCopier.create(UserDO.class, UserDTO.class, false);     UserDTO userDTO = new UserDTO();     b.copy(userDO, userDTO, null);     log.info("拷贝后,userDTO:{}", userDTO); }结果:拷贝成功18:24:24.080 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopierDemo  - 拷贝前,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T18:24:24.077, balance=100) 18:24:24.200 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopierDemo  - 拷贝后,userDTO:UserDTO(id=1, userName=Van)3.1.2 属性名称相同、类型不同目标对象属性类@Data public class UserEntity {       private Integer id;       private String userName; }测试方法/** * 属性名称相同、类型不同 */ private static void sameNameDifferentType() {     // 模拟查询出数据     UserDO userDO = DataUtil.createData();     log.info("拷贝前,userDO:{}", userDO);     BeanCopier b = BeanCopier.create(UserDO.class, UserEntity.class, false); UserEntity userEntity = new UserEntity();     b.copy(userDO, userEntity, null);     log.info("拷贝后,userEntity:{}", userEntity);}结果19:43:31.645 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopierDemo  - 拷贝前,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T19:43:31.642, balance=100) 19:43:31.748 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopierDemo  - 拷贝后,userEntity:UserEntity(id=null, userName=Van)分析
  通过日志可以发现:UserDO的int类型的id无法拷贝到UserEntity的Integer的id。3.1.3 小节
  BeanCopier只拷贝名称和类型都相同的属性。
  即使源类型是原始类型(int, short和char等),目标类型是其包装类型(Integer, Short和Character等),或反之:都不会被拷贝。3.2 自定义转换器
  通过3.1.2可知,当源和目标类的属性类型不同时,不能拷贝该属性,此时我们可以通过实现Converter接口来自定义转换器3.2.1 准备目标对象属性类@Data public class UserDomain {       private Integer id;       private String userName;         /**          * 以下两个字段用户模拟自定义转换         */       private String gmtBroth;       private String balance; }3.2.2 不使用Converter测试方法/** * 类型不同,不使用Converter */ public static void noConverterTest() {       // 模拟查询出数据       UserDO userDO = DataUtil.createData();      log.info("拷贝前,userDO:{}", userDO);       BeanCopier copier = BeanCopier.create(UserDO.class, UserDomain.class, false);   UserDomain userDomain = new UserDomain();       copier.copy(userDO, userDomain, null);       log.info("拷贝后,userDomain:{}", userDomain);}结果19:49:19.294 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopierDemo  - 拷贝前,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T19:49:19.290, balance=100) 19:49:19.394 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopierDemo  - 拷贝后,userDomain:UserDomain(id=null, userName=Van, gmtBroth=null, balance=null)分析
  通过打印日志的前后对比,属性类型不同的字段id,gmtBroth,balance未拷贝。3.2.3 使用Converter实现Converter接口来自定义属性转换public  class UserConverter implements Converter {        /**          * 时间转换的格式          */       DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");       /**         * 自定义属性转换         * @param value 源对象属性类          * @param target 目标对象里属性对应set方法名,eg.setId          * @param context 目标对象属性类        * @return          */       @Override   public Object convert(Object value, Class target, Object context) {           if (value instanceof Integer) {                     return value;            } else if (value instanceof LocalDateTime) {               LocalDateTime date = (LocalDateTime) value;               return dtf.format(date);           } else if (value instanceof BigDecimal) {                     BigDecimal bd = (BigDecimal) value;                return bd.toPlainString();            }           return value;     } }测试方法/**    * 类型不同,使用Converter    */ public static void converterTest() {       // 模拟查询出数据       UserDO userDO = DataUtil.createData();       log.info("拷贝前,userDO:{}", userDO);       BeanCopier copier = BeanCopier.create(UserDO.class, UserDomain.class, true);   UserConverter converter = new UserConverter();       UserDomain userDomain = new UserDomain();       copier.copy(userDO, userDomain, converter);       log.info("拷贝后:userDomain:{}", userDomain); }结果:全部拷贝19:51:11.989 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopierDemo  - 拷贝前,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T19:51:11.985, balance=100)3.2.4 小节一旦使用Converter,BeanCopier只使用Converter定义的规则去拷贝属性,所以在convert()方法中要考虑所有的属性。但使用Converter会使对象拷贝速度变慢。3.3 BeanCopier总结当源类和目标类的属性名称、类型都相同,拷贝没问题。当源对象和目标对象的属性名称相同、类型不同,那么名称相同而类型不同的属性不会被拷贝。注意,原始类型(int,short,char)和 他们的包装类型,在这里都被当成了不同类型,因此不会被拷贝。源类或目标类的setter比getter少,拷贝没问题,此时setter多余,但是不会报错。源类和目标类有相同的属性(两者的getter都存在),但是目标类的setter不存在,此时会抛出NullPointerException。四、BeanUtils与BeanCopier速度对比4.1 BeanUtils测试代码private static void beanUtil() {       List list = DataUtil.createDataList(10000);       long start = System.currentTimeMillis();       List dtoList = new ArrayList<>();       list.forEach(userDO -> {  UserDTO userDTO = new UserDTO();   BeanUtils.copyProperties(userDO,userDTO);   dtoList.add(userDTO);       });       log.info("BeanUtils cotTime: {}ms", System.currentTimeMillis() - start); }结果(耗时:232ms)20:14:24.380 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopyComparedDemo  - BeanUtils cotTime: 232ms 4.2 BeanCopier测试代码private static void beanCopier() {       // 工具类生成10w条数据       List doList = DataUtil.createDataList(10000);       long start = System.currentTimeMillis();       List dtoList = new ArrayList<>();       doList.forEach(userDO -> {           BeanCopier b = BeanCopier.create(UserDO.class, UserDTO.class, false);   UserDTO userDTO = new UserDTO();           b.copy(userDO, userDTO, null);           dtoList.add(userDTO);    });       log.info("BeanCopier costTime: {}ms", System.currentTimeMillis() - start); }结果(耗时:116ms)20:15:24.380 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopyComparedDemo  - BeanCopier costTime: 116ms 4.3 缓存BeanCopier实例提升性能
  BeanCopier拷贝速度快,性能瓶颈出现在创建BeanCopier实例的过程中。 所以,把创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能。测试代码private static void beanCopierWithCache() {         List userDOList = DataUtil.createDataList(10000);         long start = System.currentTimeMillis();         List userDTOS = new ArrayList<>();         userDOList.forEach(userDO -> {             UserDTO userDTO = new UserDTO();             copy(userDO, userDTO);             userDTOS.add(userDTO);         });         log.info("BeanCopier 加缓存后 costTime: {}ms", System.currentTimeMillis() - start);} public static void copy(Object srcObj, Object destObj) {         String key = genKey(srcObj.getClass(), destObj.getClass());         BeanCopier copier = null;         if (!BEAN_COPIERS.containsKey(key)) {               copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false);       BEAN_COPIERS.put(key, copier);         } else {               copier = BEAN_COPIERS.get(key);         }         copier.copy(srcObj, destObj, null); } private static String genKey(Class<?> srcClazz, Class<?> destClazz) {         return srcClazz.getName() + destClazz.getName(); }结果(耗时:6ms)20:32:31.405 [main] INFO cn.van.parameter.bean.copy.demo.BeanCopyComparedDemo  - BeanCopier 加缓存后 costTime: 6ms
  链接:https://www.jianshu.com/p/64e1d3acdc7d

荣耀Q3销量夺季军,新机卖点遭友商调侃,没有对比就没有伤害10月已经接近尾声,2021年就剩两个月了,回看一下第三季度的销量表现,毫无疑问蓝绿大厂再次引领,而排第三的不是小米苹果,而是荣耀。老实说这个数据多少有点意外,没想到荣耀在重整再出王者荣耀天美匹配机制有漏洞,玩家利用它,轻松每天升10星以任何规则都有漏洞,或者在任何规则中都有规避和利用的方法,这在游戏中也是适用的。很多时候我们会受到游戏中各种奇怪的官场规则和潜规则的影响,但如果我们能更好地理解它们,我们就不会受到束5G联合水利助力智慧水务早日来临全球网络情报报告水是生命之源。随着人类社会的发展,我们在不断地利用改造和保护水环境。中国水资源丰富。自古以来,中国就是一个水利大国和水利工程强国。古代,大禹治水,平天下。李冰修复都身边很多妈妈都在问我,儿童安全座椅有必要买吗?身边很多妈妈都在问我,儿童安全座椅有必要买吗?我个人觉得非常有必要买,车辆在行驶过程中发生碰撞时,大人是无法抱住宝宝的,而安全座椅能更好的固定住宝宝。关于怎么选安全座椅我可是做了很华为海外发布口红耳机,是理工男的浪漫还是自嗨?华为近日在海外举行了新品发布会,正式发布nova9WatchGT3等新品,主角应该是手机新品华为nova9,然而其中的一款耳机产品FreeBudsLipstick却非常亮眼,引起了随着我们生活水平的提高,大家对螨虫也是越来越在意要知道螨虫会造成呼吸道过敏和肌肤红痒,在细节上影响着我们的生活,虽然螨虫体积很小,肉眼都难以发现,但要想去除也还是有方法的。不知道大家是如何除螨的,我是既买了除螨喷雾,也入手了除螨4300元入手港版三星S10,亮屏的瞬间被惊艳到了由于各种各样的原因,三星旗舰机目前已经逐步淡出了普通消费者的视线范围,甚至有些被遗忘了。但是这并不妨碍三星仍然是安卓机皇般的存在,为什么这么说呢?亲自上手三星S10之后我才明白!我产品或涉嫌虚假宣传招致消费者投诉的娅茜优艾代理制度该如何解读在上篇文章中,我们了解了娅茜优艾一路以来的发展历程,同时看到了公司及关联企业存在的诸多问题,在本篇文章中,我们将把目光放在娅茜优艾旗下的系列产品身上,并对公司推出的四级代理制度进行网友花32888买定制版iPhone12Mini,上手的瞬间,真值送礼送礼,就是要送到对方的心坎里。前段时间我同学的女朋友要过生日,同学让我帮他挑一款送礼用的手机。由于同学是个富二代,价格无所谓,但是拿出去必须有面子。而且女方也是LV古驰等名牌包1499起售的红米Note10Pro,到底阉割了什么?用了3天后答案很明显红米Note10Pro终于发布了,笔者由于提前拿到了机器,所以基本没看发布会,就等最后的售价了。当发布会公布1499元起的售价后,我还是挺震撼的,因为我一直把它当做一款2000元档直播带货又翻车了,老罗第五次创业又遇挫折说起直播带货,想必许多人都不陌生。近几年来的直播带货可以说是火热到爆炸,无论是网红还是明星,全都涌入进直播带货的行业中,希望能够从中分得一杯羹。然而直播带货翻车事件还真不少,前段时
MotoEdge30Pro渲染图亮相,edgeS首当其冲跌至白菜价让路MySmartPrice刚刚在分享上发布了来自Sudhanshu的新消息,展示了传说中的MotoEdge30Pro智能手机的渲染图。据传该机器很可能是MotoX30的马甲,配有前置外包开发APP费用详解平时我们做过各行各业的APP,针对APP开发的过程中涉及的各种费用也是比较了解的,常见的APP主要有这些,比如商城APP教育APP社交APP协同APP办公APP医疗APP等等。北京TIAPortal(博图)程序块类型一程序块分类1。用户块用户块包括程序代码和用户数据。在结构化程序中,一些块循环调用处理,一些块需要时才调用。2。系统块系统块是在CPU操作系统中预先定义好的功能和功能块。这些块不占艾登参与的国家科技创新2030新一代人工智能项目获批立项导语近年来,国务院工信部药监局等部委颁布系列政策文件,大力推广人工智能技术在医疗各细分领域的应用。为落实国务院新一代人工智能发展规划,科技部启动实施了科技创新2030新一代人工智能全民K歌通过工信部首批适老化及无障碍水平评测为着力解决老年人残疾人等特殊群体在使用互联网等智能技术时遇到的困难,此前工信部开展了互联网应用适老化及无障碍改造专项行动。近日,工信部公布了首批通过适老化及无障碍水平评测的网站和APageHelper使用ThreadLocal的线程复用问题,你用对了吗?前言PageHelper是较为常用的分页插件,通过实现Mybatis的Interceptor接口完成对querysql的动态分页,其中分页参数由ThreadLocal进行保存。简单要想科学瘦得快体脂秤先安排,新老三款体脂秤一手横评作为一个年过而立之年的互联网新晋搬砖工,工作一年多来显著感觉自己是膨胀了。每天996雷打不动,公司食堂油水还特别足,肉眼可见得整个人心宽体胖了。正所谓,工欲善其事,必先利其器。作为数字货币再发威,补涨龙头会是恒宝股份恒宝股份一直是我推荐给大家的重点,今天强势涨停,在长期主力资金的连续推动下,当前走主升行情值得期待,为什么恒宝会是补涨龙头呢,超越翠微股份,成为市场焦点,拥有超强硬核逻辑如下1。正立讯精密(002475)涨超5,物联网龙头ETF(159896)盘中涨1。68金融界1月24日消息,截至1408,物联网龙头ETF(159896)上涨1。36,盘中一度涨1。68交投活跃,换手率达11。85。成分股方面,国科微(300672)涨7。02,欣旺再也不怕手机号被注册!国内运营商巨头推出新招烦恼又少了一个1月23日,中国联通联合中国信息通信研究院号码服务推进组与部分互联网企业,推出互联网账号清理服务,有望替代现有的账号解绑与申诉流程,帮助用户快速解决新手机号被注册的问题。微博抖音今个人感觉vivo手机同样的配置比小米的要流畅,大家同意吗?看到这句话很多网友都不屑一顾,同配置的vivo手机和小米手机价格能一样么?要搁以前可能还不好说,但要说现在,那iQOO真不怵小米,不管是堆配置还是比价格,iQOO都来者不拒。一句话