对象拷贝类
2016-02-16 13:37
501 查看
对象拷贝类
较流行工具类 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-PropertyUtils | Apache-BeanUtils | Spring-BeanUtils | Cglib-BeanCopier |
---|---|---|---|---|
是否可以扩展useConvete功能 | no | yes | yes | yes,但比较难用 |
(sourceObject,targetObject)的顺序 | 逆序 | 逆序 | ok | ok |
对sourceObject特殊属性的限制:(Date,BigDecimal等)[见备注1] | ok | no,异常出错 | ok | ok |
相同属性名,且类型不匹配时候的处理[见备注2] | 异常,拷贝部分属性,非常危险 | ok,并能进行初级转换,Long和Integer互转 | 异常,拷贝部分属性 | ok,但是该属性不拷贝 |
Get和set方法不匹配的处理[见备注3] | ok | ok | ok | 创建拷贝的时候报错,无法拷贝任何属性(当且仅当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; imp 4000 ort 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; } }
相关文章推荐
- java类型 -- 拷贝
- 画柱状图Java
- JAXWS 开发webService ,使用spring注入service类时,无法注入,报空指针
- JAVA环境变量的搭建
- JPA criteria 查询:类型安全与面向对象
- Spring使用Cache
- Java描述贪心算法解决背包问题
- Spring Data JPA入门
- cast——java类型转换
- java基础-----2Java 发展简史
- 2015年度总结--javaWeb
- WebCollector教程——在Eclipse项目中配置使用WebCollector爬虫
- WebCollector网页正文提取
- WebCollector设置代理
- JAVA爬虫WebCollector 2.x入门教程——基本概念
- WebCollector教程——获取当前深度
- WebCollector教程——爬取搜索引擎
- WebCollector教程——爬取新浪微博
- Lazy爬虫配置教程
- WebCollector分布式爬取