zoukankan      html  css  js  c++  java
  • 对象属性拷贝工具类大全==>Bean的属性拷贝从此不用愁

    大家在做java开发时,肯定会遇到api层参数对象传递给服务层,
    或者把service层的对象传递给dao层,他们之间又不是同一个类型对象,
    但字段又是一样,如果还是用普通的get、set方式来处理话,比较繁琐,.... 那么你来跟我学....."天下武功,唯快不破"===>要的就是快....
    1.使用get/set不用说了,字段一多脑壳大....
    2.BeanCopier 使用了cglib的修改字节码,真的是动态Read Writer getter/setter 快的一逼啊 源码过于简单 直接上代码 我使用的最基本的,大家可以自定义Converter,定以后完全按照Converter转换
    需要注意的是:
    * 1.当源类和目标类的属性名称、类型都相同,拷贝结果棒棒哒。
    * 2.当源对象和目标对象的属性名称相同、类型不同,那么名称相同而类型不同的属性不会被拷贝。另外注意,原始类型(int,short,char)和 他们的包装类型,在这里都被当成了不同类型。因此不会被拷贝。
    * 3.源类或目标类的setter比getter少,拷贝没问题,此时setter多余,但是不会报错。
    * 4.源类和目标类有相同的属性(两者的getter都存在),但是目标类的setter不存在, 此时会抛出NullPointerException(这个在高版本bug已经修改测试通过,我使用的49.0)
     1 /**
     2  * @author LiJing
     3  * @ClassName: BeanCopyUtils
     4  * @Description: 基于BeanCopier的属性拷贝
     5  * @date 2019/4/17 9:1521  * * 凡是和反射相关的操作,基本都是低性能的。凡是和字节码相关的操作,基本都是高性能的。
    22  * <p/>
    23  */
    24 @Slf4j
    25 public class BeanCopyUtils {
    26 
    27     /**
    28      * 创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能
    29      */
    30     private static final Map<String, BeanCopier> BEAN_COPIERS = new ConcurrentHashMap<>();
    31 
    32     /**
    33      * 该方法没有自定义Converter,简单进行常规属性拷贝
    34      *
    35      * @param srcObj  源对象
    36      * @param destObj 目标对象
    37      */
    38     public static void copy(final @NotNull Object srcObj, final @NotNull Object destObj) {
    39         String key = genKey(srcObj.getClass(), destObj.getClass());
    40         BeanCopier copier;
    41         if (!BEAN_COPIERS.containsKey(key)) {
    42             copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false);
    43             BEAN_COPIERS.put(key, copier);
    44         } else {
    45             copier = BEAN_COPIERS.get(key);
    46         }
    47         copier.copy(srcObj, destObj, null);
    48     }
    49 
    50     private static String genKey(Class<?> srcClazz, Class<?> destClazz) {
    51         return srcClazz.getName() + destClazz.getName();
    52     }
    53 }
    
    
    3.接下来再来玩一个org.springframework.beans.BeanUtils,这个比较骚,使用反射,反射就比较慢了,要加载字节码,反编译,再实例化,再映射属性.....来来上二斤代码,先吃着 有用的地方不多大家看着用哈<copyProperties>其实有这个方法就够了
      1 import lombok.extern.slf4j.Slf4j;
      2 import org.springframework.beans.BeanUtils;
      3 import org.springframework.beans.BeansException;
      4 import org.springframework.beans.FatalBeanException;
      5 import org.springframework.util.Assert;
      6 import org.springframework.util.ClassUtils;
      7 17 
     18 /**
     19  * @author LiJing
     20  * @ClassName: BeanUtils
     21  * @Description: 属性拷贝工具类
     22  * @date 2018/4/10 11:54
     23  */
     24 @Slf4j
     25 public class MyBeanUtils extends BeanUtils {
     26 
     27     /**
     28      * 从org.springframework.beans.BeanUtils类中直接复制过来
     29      *
     30      * @param source
     31      * @param target
     32      * @throws BeansException
     33      */
     34     public static void copyProperties(Object source, Object target) throws BeansException {
     35         copyProperties(source, target, null, (String[]) null);
     36     }
     37 
     38     /**
     39      * 从org.springframework.beans.BeanUtils类中直接复制过来,修改部分代码,不为null才复制
     40      *
     41      * @param source
     42      * @param target
     43      * @param editable
     44      * @param ignoreProperties
     45      * @throws BeansException
     46      */
     47     private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)
     48             throws BeansException {
     49 
     50         Assert.notNull(source, "Source must not be null");
     51         Assert.notNull(target, "Target must not be null");
     52 
     53         Class<?> actualEditable = target.getClass();
     54         if (editable != null) {
     55             if (!editable.isInstance(target)) {
     56                 throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
     57                         "] not assignable to Editable class [" + editable.getName() + "]");
     58             }
     59             actualEditable = editable;
     60         }
     61         PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
     62         List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
     63 
     64         for (PropertyDescriptor targetPd : targetPds) {
     65             Method writeMethod = targetPd.getWriteMethod();
     66             if (writeMethod != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
     67                 PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
     68                 if (sourcePd != null) {
     69                     Method readMethod = sourcePd.getReadMethod();
     70                     if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
     71                         try {
     72                             if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
     73                                 readMethod.setAccessible(true);
     74                             }
     75                             Object value = readMethod.invoke(source);
     76                             // 判断被复制的属性是否为null, 如果不为null才复制
     77                             if (value != null) {
     78                                 if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
     79                                     writeMethod.setAccessible(true);
     80                                 }
     81                                 writeMethod.invoke(target, value);
     82                             }
     83                         } catch (Throwable ex) {
     84                             throw new FatalBeanException(
     85                                     "不能拷贝属性 '" + targetPd.getName() + "' 从原对象给目标对象,详细原因见:", ex);
     86                         }
     87                     }
     88                 }
     89             }
     90         }
     91     }
     92 
     93 
     94     /**
     95      * 构造函数.
     96      */
     97     public MyBeanUtils() {
     98         throw new RuntimeException("this is a util class,can not instance");
     99     }
    411 }
    4.还有很多属性拷贝的方法例如org.apache.commons.beanutils.BeanUtils的copyProperties(Object dest, Object src)这里不举例了 类似于Spring的属性拷贝
    5.还有使用easymapper来进行对象映射,但在项目中存在不稳性,偶尔出现映射不上的问题,会报异常com.baidu.unbiz.easymapper.exception.MappingException,大家可自行研究
    其实有很多种方法进行属性拷贝的,例如dozer等等 下面看下测试性能吧:以:万级进行测试,我觉得Cglib太给力了.可以在遇到属性拷贝瓶颈时考虑.当然他们各有优点哈,功能也不尽相同.还需要多使用体会.
    输出结果:手动Copy > cglibCopy > springBeanUtils > apachePropertyUtils > apacheBeanUtils 可以理解为: 手工复制 > cglib > 反射 > Dozer。
    类型Framework 测试性能(10000调用次)time
    Pure get/set 10ms
    Easy mapper 46ms
    Cglib Beancopier 11ms
    Spring BeanUtils 94ms
     Apache BeanUtils 249ms
     Apache PropertieyUtils 130ms
    Dozer 770ms
     


    一切没有栗子的讲解都是耍流氓

  • 相关阅读:
    Python爬取数据(基础,从0开始)
    个人作业——软件测评
    结对第二次作业
    结对第一次作业
    寒假作业(2/2)
    个人作业———软工实践课程总结
    Axios 介绍和使用
    软件评测
    结对第二次作业
    结对第一次——疫情统计可视化(原型设计)
  • 原文地址:https://www.cnblogs.com/xxmyz/p/10722463.html
Copyright © 2011-2022 走看看