对象拷贝类

对象拷贝类

较流行工具类 PropertyUtils,BeanUtils,BeanCopier:
Apache的两个版本:(反射机制)
org.apache.commons.beanutils.PropertyUtils.copyProperties(Object dest, Object orig)
org.apache.commons.beanutils.BeanUtils.copyProperties(Object dest, Object orig)
Spring版本:(反射机制)
org.springframework.beans.BeanUtils.copyProperties(Object source, Object target, Class editable, String[] ignoreProperties)
cglib版本:(使用动态代理,效率高)
net.sf.cglib.beans.BeanCopier.copy(Object paramObject1, Object paramObject2, Converter paramConverter)

原理简介:
反射类型:(apache)
都使用静态类调用,最终转化虚拟机中两个单例的工具对象。
public BeanUtilsBean()
{
this(new ConvertUtilsBean(), new PropertyUtilsBean());
}
ConvertUtilsBean可以通过ConvertUtils全局自定义注册。
ConvertUtils.register(new DateConvert(), java.util.Date.class);
PropertyUtilsBean的copyProperties方法实现了拷贝的算法。
1、 动态bean:orig instanceof DynaBean:Object value = ((DynaBean)orig).get(name);然后把value复制到动态bean类
2、 Map类型:orig instanceof Map:key值逐个拷贝
3、 其他普通类::从beanInfo【每一个对象都有一个缓存的bean信息,包含属性字段等】取出name,然后把sourceClass和targetClass逐个拷贝
Cglib类型:BeanCopier
copier = BeanCopier.create(source.getClass(), target.getClass(), false);
copier.copy(source, target, null);

Create对象过程:产生sourceClass-》TargetClass的拷贝代理类,放入jvm中,所以创建的代理类的时候比较耗时。最好保证这个对象的单例模式,可以参照最后一部分的优化方案。
创建过程:源代码见jdk:net.sf.cglib.beans.BeanCopier.Generator.generateClass(ClassVisitor)
1、 获取sourceClass的所有public get 方法-》PropertyDescriptor[] getters
2、 获取TargetClass 的所有 public set 方法-》PropertyDescriptor[] setters
3、 遍历setters的每一个属性,执行4和5
4、 按setters的name生成sourceClass的所有setter方法-》PropertyDescriptor getter【不符合javabean规范的类将会可能出现空指针异常】
5、 PropertyDescriptor[] setters-》PropertyDescriptor setter
6、 将setter和getter名字和类型 配对,生成代理类的拷贝方法。
Copy属性过程:调用生成的代理类,代理类的代码和手工操作的代码很类似,效率非常高。

常用工具比较:

功能Apache-PropertyUtilsApache-BeanUtilsSpring-BeanUtilsCglib-BeanCopier
是否可以扩展useConvete功能noyesyesyes,但比较难用
(sourceObject,targetObject)的顺序逆序逆序okok
对sourceObject特殊属性的限制:(Date,BigDecimal等)[见备注1]okno,异常出错okok
相同属性名,且类型不匹配时候的处理[见备注2]异常,拷贝部分属性,非常危险ok,并能进行初级转换,Long和Integer互转异常,拷贝部分属性ok,但是该属性不拷贝
Get和set方法不匹配的处理[见备注3]okokok创建拷贝的时候报错,无法拷贝任何属性(当且仅当sourceClass的get方法超过set方法)

一些优化和改进

增强apache的beanUtils的拷贝属性,注册一些新的类型转换
public class BeanUtilsEx extends BeanUtils
{
  public static void copyProperties(Object dest, Object orig)
  {
    try
    {
      BeanUtils.copyProperties(dest, orig);
    } catch (IllegalAccessException ex) {
      ex.printStackTrace();
    } catch (InvocationTargetException ex) {
      ex.printStackTrace();
    }
  }
  static
  {
    ConvertUtils.register(new DateConvert(), java.util.Date.class);
    ConvertUtils.register(new DateConvert(), java.sql.Date.class);
    ConvertUtils.register(new BigDecimalConvert(), BigDecimal.class);
  }
}
将beancopier做成静态类,方便拷贝
public class BeanCopierUtils {
     public static Map<String,BeanCopier> beanCopierMap = new HashMap<String,BeanCopier>();

     public static void copyProperties(Object source, Object target){
         String beanKey =  generateKey(source.getClass(), target.getClass());
         BeanCopier copier =  null;
         if(!beanCopierMap.containsKey(beanKey)){
              copier = BeanCopier.create(source.getClass(), target.getClass(), false);
              beanCopierMap.put(beanKey, copier);
         }else{
              copier = beanCopierMap.get(beanKey);
         }
         copier.copy(source, target, null);
     }   
     private static String generateKey(Class<?> class1,Class<?>class2){
         return class1.toString() + class2.toString();
     }
}
修复beanCopier对set方法强限制的约束
改写net.sf.cglib.beans.BeanCopier.Generator.generateClass(ClassVisitor)方法
将133行的
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
预先存一个names2放入
/* 109 */       Map names2 = new HashMap();
/* 110 */       for (int i = 0; i < getters.length; ++i) {
/* 111 */         names2.put(setters[i].getName(), getters[i]);
/*     */       }
调用这行代码前判断查询下,如果没有改writeMethod则忽略掉该字段的操作,这样就可以避免异常的发生。

关于BeanCopier的一些思考

线程安全的例子

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.DigestUtils;

public class ConvertBeanUtil {
    private static  Logger           logger = LoggerFactory.getLogger(LOG_TYPE.PAFF_SERVICE.val);
    private static ConcurrentHashMap<String,BeanCopier> cache=new ConcurrentHashMap<String, BeanCopier>();


    /**
     * @param source 源对象class
     * @param target 目标对对象class
     * @param sourceObj 复制的源对象
     * @param useConverter 
     * @return 
     * @throws Exception
     */
    public static <T> T copyBeanProperties(@SuppressWarnings("rawtypes") Class source,Class<T> target,Object sourceObj, boolean useConverter) {
        if(sourceObj==null) return null;
        T t;
        try {
            t = target.newInstance();
        } catch (Exception e) {
            logger.error("", e);
            return null;
        }
        String key=source.getSimpleName()+target.getSimpleName();
        BeanCopier copier = cache.get(key);
        if(copier==null){
             copier=createBeanCopier(source, target, useConverter, key);
        }
        copier.copy(sourceObj, t, null);
        return t;
    }
    /**
     * 
     * 
     * @param sourceObj  源对象
     * @param target  目标对象
     * @param useConverter 
     * @return
     * @throws Exception
     */
    public static <T> T copyBeanProperties(Object sourceObj, T target){
        return copyBeanProperties(sourceObj, target, false);
    }
    /**
     * 
     * 
     * @param sourceObj  源对象
     * @param target  目标对象
     * @param useConverter 
     * @return
     * @throws Exception
     */
    public static <T> T copyBeanProperties(Object sourceObj, T target, boolean useConverter) {
        if(sourceObj==null||target==null) return null;
        String key=sourceObj.getClass().getSimpleName()+target.getClass().getSimpleName();
        BeanCopier copier = cache.get(key);
        if(copier==null){
             copier=createBeanCopier(sourceObj.getClass(), target.getClass(), useConverter, key);
        }
        copier.copy(sourceObj, target, null);
        return target;
    }

    public static <T> List<T> copyListBeanPropertiesToList(List<?> sourceObjs,List<T> targets,Class<T> targetType){
        if(sourceObjs==null||targets==null||targetType==null) return null;
        T t;
        for(Object o:sourceObjs){
            try {
                t=targetType.newInstance();
                targets.add(copyBeanProperties(o,t,false));
            } catch (InstantiationException e) {
                logger.error("", e);
            } catch (IllegalAccessException e) {
                logger.error("", e);
            }
        }
        return targets;
    }
    /**
     * 
     * Pagination对象转换
     * @param sourceObjs
     * @param targets
     * @param targetType
     * @return
     */
    public static <T> PaginationBean<T> copyPageBeanPropertiesToPageBeanList(PaginationBean<?> sourceObjs,PaginationBean<T> targets,Class<T> targetType){

        if(sourceObjs==null||targets==null||targetType==null||sourceObjs.getPageList()==null) return null;
        targets = copyBeanProperties(sourceObjs, targets, false);
        List<T> tlist = new ArrayList<T>();
        T t;
        for(Object o:sourceObjs.getPageList()){
            try {
                t=targetType.newInstance();
                tlist.add(copyBeanProperties(o,t,false));
            } catch (InstantiationException e) {
                logger.error("", e);
            } catch (IllegalAccessException e) {
                logger.error("", e);
            }
        }
        targets.setPageList(tlist);
        return targets;
    }

    @SuppressWarnings("unused")
    private static String getHashKey(String str){
        if(str==null) return null;
        return DigestUtils.md5DigestAsHex(str.getBytes());
    }

    @SuppressWarnings({"rawtypes" })
    private static BeanCopier createBeanCopier(Class sourceClass,Class targetClass,boolean useConverter,String cacheKey){
        BeanCopier copier = BeanCopier.create(sourceClass,targetClass, useConverter);
        cache.putIfAbsent(cacheKey, copier);
        return copier;
    }    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值