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

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

德国博世集团董事会主席中国继续保持博世集团最大的单一市场中新网柏林5月10日电(记者马秀秀)中国继续保持博世集团最大的单一市场。德国工业巨头博世集团董事会主席史蒂凡哈通(StefanHartung)10日表示,长期而言,博世将继续坚持本网络文化经营许可证一什么是网络文化经营许可证?网络文化经营许可证简称文网文,是指须由公司所在地文化厅(局)颁发批准,从事经营性互联网文化活动的互联网信息服务提供者,其中经营性互联网文化活动是指以营利便携防水全金属低音炮DUVOSS德渥D1蓝牙小音箱体验分享IEC强乐电子是一家老牌影音设备厂家,致力于全方位的影音生活解决方案,产品包括蓝牙耳机超薄音箱等。秉承着精益制造的发展方向,旗下的德渥DUVOSS品牌也开始向更薄更智能的未来音频领铭科精技登陆深市主板致力于成为全球领先精密模具及金属结构件提供商中证网讯(王珞)5月12日,铭科精技(001319)正式在A股主板挂牌上市。铭科精技专业从事精密冲压模具和金属结构件的研发生产和销售,拥有优秀的模具设计开发能力制造和精密冲压技术,建站软件生态里可能长出SaaS独角兽吗?编辑导语许多DTCSaaS产品正从建站软件的生态中生长出来,正如许多中国SaaS产品从钉钉生态中生长出来。本文作者将分享近期对DTCSaaS的观察和认知及DTCSaaS产品发展情况Kotlin继承Kotlin继承Kotlin中所有类都继承该Any类,它是所有类的超类,对于没有超类型声明的类是默认超类classExample从Any隐式继承Any默认提供了三个函数equals科技早报国美CEO王巍回应惠而浦指控马斯克收购Twitter交易暂时搁置国美CEO王巍回应惠而浦指控对方刻意歪曲诋毁,不排除提起反诉近日,惠而浦发布公告控诉国美电器长期拖欠货款,且拖欠情况持续恶化,决定终止与国美电器的商务合作。随后,国美否认存在延迟支云米携手抖音重磅推出智能清净家,轻松打造一屋子的高科技5月13日,盛夏来临之前,云米携手抖音心动家重磅推出云米智能清净家主题活动,旨在通过开启家电转型场景消费模式的新赛道,深度满足用户在室内温度调节水质净滤全屋清洁以及囤货洗涤等居家生电信日特别微视频丨这个517,让我们把C位留给他们人民邮电报,赞124第53个世界电信和信息社会日即将到来,国际电联将今年的主题定为面向老年人和实现健康老龄化的数字技术,高度体现了数字经济时代,信息通信行业助力老年人跨越数字鸿沟的星链系统被攻击马斯克暂停收购推特是暂时的还是永久据俄通讯社消息,马斯克声明指出推特收购交易暂停,正在等待确认垃圾邮件和虚假账户占实际用户比例确实在5以下的详细信息。2020年美国大选,特朗普在竞争总统中败给了拜登,随后否认大选结不止天玑8100MAX!一加Ace竞速版外观曝光居中Logo圆润机身现如今,手机迭代速度快得让人应接不暇,几乎每款手机新品亮相都会在外观设计或者配置调优方面有所突破。比如定位为有颜有料的性能王牌一加Ace,发布上市不足一月就凭借出色的性能和质感收获
双十一迎来开门红云米21Face2智能冰箱意料之中成爆款刚进入十一月,各大电商平台就迎来了双十一开门红。各种史低好价纷沓而至,想知道屏幕前的你,此刻钱包还捂得住吗?必须承认的是,今年双十一的提前到来给了我们更多的时间和机会,不必像往年那多平台智能识别,自定义一键连招,北通阿修罗2Pro体验测评双十一已经到来,大家最近都在买些什么呢?作为一个游戏玩家,在许多人都在囤日常用品的情况下,我最近都在关注各种游戏设备,手柄就是其中之一。而非常好运的是,在我准备下单买新手柄前,我拿只有2K预算怎么选电视?电影级4K画质杜比音效只要1999元同往年的双11大促一样,家电类大件产品受到了消费者们的欢迎。而在今年双11期间,在各大手机厂商的纷纷入局下,智能电视行业更是迎来大革新,无论是产品力上,还是体验和性价比方面,都堪称实现元宇宙,对我们普通人的影响也巨大最近,网上又出现了新的词汇元宇宙(metaverse),facebook的扎克伯格在一次演讲中,16次提到元宇宙,并提出建立企业元宇宙的方案。字节跳动也花费了大量资金收购了虚拟现实黑加手表上手测评,21天超长续航AI心情表盘,产品力爆棚近几年,智能穿戴设备逐渐受到大众的认可,各家数码厂商的智能手表手环也是层出不穷,而除了比较知名的大品牌以外,很多小众品牌推出的智能穿戴设备也是很不错的,而黑加就是其中之一,尽管前几充电打火机没听说过?极蜂电弧充电打火机有颜有料,就是这么酷打火机应该是生活中比较常见的物件了,尽管很多家庭可能没有人抽烟,但是它也是必备的。因为应急用时,它显得非常关键,比如炉灶没电了,需要点火才能打燃时,家中没有打火机那可就只有出去吃了特斯拉没入围,保时捷垫底,富豪买电动跑车都有什么选择前两天,特斯拉首席执行官埃隆马斯克回复了一位推特用户,确认了一件事情特斯拉跑车Roadster再次被推迟了。根据马斯克的回复,似乎Roadster要推迟到2023年才能发货。就算发iQOO双十一推出新玩法,携手薇娅引爆直播间发展至今,直播带货已然成为最受人们欢迎的购物方式之一。而在直播带货中,大家对薇娅一定不陌生,其在电商直播中一直处于领先地位。如今正值双十一购物狂欢期间,薇娅的直播间也异常火热。值得双十一选生产力工具,为什么我不推荐入手新款MacBookPro?如今正值双十一期间,相信不少人和我一样在打算更新自己的笔记本。而作为一个视频工作的用户,我对于笔记本的要求要苛刻不少,不仅仅要高素质屏幕和不俗的性能帮助我更好的渲染视频,而且最后还单耳3。2g14。2mm超大喇叭,UampampampI由我Mini上手体验如何?都知道科技的发展最大优势就是能够给用户带来更为便捷的生活体验,而这种便利有可能会是产品的体验上,也有可能是产品的形态方面比如无线耳机鼠标键盘音箱等。事实上也的确如此,没有了线材的限满血续航王iQOOZ5正式发布,预售期到手价仅1799元起2021年9月23日iQOO新品发布会于线上召开,新一代性能先锋iQOOZ5正式发布。iQOOZ5配备满血强悍性能配置,带来高性能长续航及畅爽娱乐体验,iQOO更是首次将旗舰机专属