Java对象copy之BeanCopier⼯具类的使⽤
业务系统中经常需要两个对象进⾏属性的拷贝,不能否认逐个的对象拷贝是最快速最安全的做法,但是当数据对象的属性字段数量超过程序员的容忍的程度,代码因此变得臃肿不堪,使⽤⼀些⽅便的对象拷贝⼯具类将是很好的选择。
Apache的两个版本:(反射机制)
org.apachemons.pyProperties(Object dest, Object orig)
org.apachemons.pyProperties(Object dest, Object orig)
Spring版本:(反射机制)
org.springframework.pyProperties(Object source, Object target, Class editable, String[] ignoreProperties)
cglib版本:(使⽤动态代理,效率⾼)
lib.py(Object paramObject1, Object paramObject2, Converter paramConverter)
都使⽤静态类调⽤,最终转化虚拟机中两个单例的⼯具对象。
public BeanUtilsBean()
{
this(new ConvertUtilsBean(), new PropertyUtilsBean());
}
ConvertUtilsBean可以通过ConvertUtils全局⾃定义注册。
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逐个拷贝
类型:BeanCopier
copier = Class(), Class(), false);
Create对象过程:产⽣sourceClass-》TargetClass的拷贝代理类,放⼊jvm中,所以创建的代理类的时候⽐较耗时。最好保证这个对象的单例模式,可以参照最后⼀部分的优化⽅案。
创建过程:源代码见jdk:lib.beans.ateClass(ClassVisitor)
1、  获取sourceClass的所有public get ⽅法-》PropertyDescriptor[] getters
2、  获取TargetClass 的所有 public set ⽅法-》PropertyDescriptor[] setters
3、  遍历setters的每⼀个属性,执⾏4和5
4、  按setters的name⽣成sourceClass的所有setter⽅法-》PropertyDescriptor getter【不符合javabea
n规范的类将会可能出现空指针异常】
5、  PropertyDescriptor[] setters-》PropertyDescriptor setter
6、  将setter和getter名字和类型 配对,⽣成代理类的拷贝⽅法。
Copy属性过程:调⽤⽣成的代理类,代理类的代码和⼿⼯操作的代码很类似,效率⾮常⾼。
上述这⼏种⽅式速度最快的是BeanCopier,默认只复制名称和类型相同的字段,还会对date为空的情况不进⾏复制。
我认为这样做最好,⽐如对象A的值复制到B中,我们把相同的进⾏复制,把不同的,也就是需要我们个性化的⼀些字段,单独出来⽤get来赋值,这样程序就会很明确,重点也就聚焦在了不同的地⽅。
陷阱条件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⽅法)
System.out.flectionToString(from));
System.out.flectionToString(to));
}
}
Get和set⽅法不匹配的处理
public class ErrorBeanCopierTest {
/**
* 从该⽤例看出ate的target.class 的每⼀个get⽅法必须有队形的set⽅法
* @param args
*/
public static void main(String args[]) {
BeanCopier copier = ate(UnSatifisedBeanCopierObject.class, SourceClass.class,false);
copier = ate(SourceClass.class, UnSatifisedBeanCopierObject.class, false); //此处抛出异常创建    }
}
class UnSatifisedBeanCopierObject {
object to的beanUtils的拷贝属性,注册⼀些新的类型转换
public class BeanUtilsEx extends BeanUtils
{
public static void copyProperties(Object dest, Object orig)
{
try
{
} catch (IllegalAccessException ex) {
ex.printStackTrace();
} catch (InvocationTargetException ex) {
ex.printStackTrace();
}
}
static
{
}
}
将beancopier做成静态类,⽅便拷贝
public class BeanCopierUtils {
public static Map<String,BeanCopier> beanCopierMap = new HashMap<String,BeanCopier>();
public static void copyProperties(Object source, Object target){
String beanKey =  Class(), Class());
BeanCopier copier =  null;
if(!ainsKey(beanKey)){
copier = Class(), Class(), false);
beanCopierMap.put(beanKey, copier);
}else{
copier = (beanKey);
}
}
private static String generateKey(Class<?> class1,Class<?>class2){
String() + String();
}
}
修复beanCopier对set⽅法强限制的约束
改写lib.beans.ateClass(ClassVisitor)⽅法
将133⾏的
MethodInfo write = WriteMethod());
预先存⼀个names2放⼊
/* 109 */      Map names2 = new HashMap();
/* 110 */      for (int i = 0; i < getters.length; ++i) {
/* 111 */        names2.put(setters[i].getName(), getters[i]);
/*    */      }
调⽤这⾏代码前判断查询下,如果没有改writeMethod则忽略掉该字段的操作,这样就可以避免异常的发⽣。下⾯我封装⼀下(⼤家可以直接⽤):
public class BeanCopierUtils {
public static Map<String,BeanCopier> beanCopierMap = new HashMap<String,BeanCopier>();
public static void copyProperties(Object source, Object target){
String beanKey =  Class(), Class());
BeanCopier copier =  null;
if(!ainsKey(beanKey)){
copier = Class(), Class(), false);
beanCopierMap.put(beanKey, copier);
}else{
copier = (beanKey);
}
}
private static String generateKey(Class<?> class1,Class<?>class2){
String() + String();
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。