您的位置:首页 > 编程语言 > Java开发

对象拷贝类

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-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;
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拷贝