首页 > 编程知识 正文

beanutils.copyproperties 深拷贝,cglib和jdk动态代理效率

时间:2023-05-05 19:11:18 阅读:49807 作者:1181

关注收藏是我创作的最大动力

大家可能用各种方法复制过bean。 有很多类型,包括第一个xsdsj的BeanUtil、Spring的BeanUtil和MapStruct。 它们都有各自的实现方法,反射可以生成映射,也可以生成字节码。

可以说每种方式都有各自的优缺点。 (意思是不评价那些好坏。 )今天我只谈cglib的BeanCopier,它有什么特点呢? 因为生成字节码并生成调用set get的方法,所以非常高效。 (实际上生成的代码是b.setxx (a.getax ) ) ) ) )。

如果属性名称匹配但类型不匹配,则支持自定义转换

仅支持符合javaBean规范的属性

复制值,即只有最外面的对象是新的,所有属性都使用原始对象的值(而不是深度复制)。

没错,其最大的特点是生成字节码。

让我们来看看它的使用方法:

beancopierbeancopier=bean copier.create (source class,targetClass,false );

bean copier.copy (源,目标,空);

首先,生成BeanCopier实例。 继承BeanCopier的子类由cglib生成。

在第2步中,调用复制方法。

如果缓存了BeanCopier实例,则此copy方法将变得更快(只是getter和setter )。 另外,您是否有过一点思考,如果需要复制的对象,并且目标类型没有参与结构,该怎么办?

以下是可以很好地解决此问题的objenesisstd (将其集成到spring中)。

在官方网站文档中,Objenesis尝试使用多种方法实例化对象,具体取决于对象类型、JVM版本、JVM供应商和安全管理器。

还没结束。 如果尝试在bean和map之间相互旋转,则BeanCopier不满意。 但是,cglib还有一个BeanMap。 BeanMap是map的实现,我们可以将其用作map。

所以,试着写一下beanCopy的包吧。 是这样的(代码中没有多余的部分)。

公共财务类beancopyutil {

私密beancopyutil

}

privatestaticthreadlocalobjenesisstdthreadlocal=thread local.with initial (objenesisstd 3360: new );

私有静态ConcurrentHashMap,ConcurrentHashMap,beancopiercache=newconcurrenthashmap (;

publicstatictcopy (对象源,类目标) {

返回复制(source,objenesisStdThreadLocal.get ).newinstance );

}

publicstatictcopy (对象源,T target ) {

beancopierbeancopier=getcachebeancopier (source.getclass (,target.getClass ) );

bean copier.copy (源,目标,空);

返回目标;

}

publicstaticlistcopylist (列表源,类目标) {

if(sources.isempty () ) ) )。

return Collections.emptyList (;

}

ArrayList list=new ArrayList (sources.size );

objenesisstdobjenesisstd=objenesisstdthreadlocal.get (;

对象源:源(for ) {

if(source==null ) {

thrownewapiexception (syscode.sys 2000;

}

tnewinstance=objenesisstd.new instance (target;

beancopierbeancopier=getcachebeancopier (source.getclass ),target );

信标拷贝

r.copy(source, newInstance, null);

list.add(newInstance);

}

return list;

}

public static T mapToBean(Map, ?> source, Class target) {

T bean = objenesisStdThreadLocal.get().newInstance(target);

BeanMap beanMap = BeanMap.create(bean);

beanMap.putAll(source);

return bean;

}

public static Map, ?> beanToMap(T source) {

return BeanMap.create(source);

}

private static BeanCopier getCacheBeanCopier(Class source, Class target) {

ConcurrentHashMap, BeanCopier> copierConcurrentHashMap = cache.computeIfAbsent(source, aClass -> new ConcurrentHashMap<>(16));

return copierConcurrentHashMap.computeIfAbsent(target, aClass -> BeanCopier.create(source, target, false));

}

}

上面只是一个参考,建议大家自己看下cglib的bean包源码,很有意思,生成字节码的部分在net.sf.cglib.beans.BeanCopier.Generator#generateClass。

我上面说过BeanCopier只支持符合javaBean规范的,原因在这里:

在generateClass方法中,通过ReflectUtils.getBeanGetters获取源类型和目标类型的getter方法

PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(this.source);

PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(this.target);

Map names = new HashMap();

for(int i = 0; i < getters.length; ++i) {

names.put(getters[i].getName(), getters[i]);

}

...

for(int i = 0; i < setters.length; ++i) {

...

}

而ReflectUtils虽然看名字是反射工具类,但getBeanGetters方法是通过反省来拿到对应方法的,

BeanInfo info = Introspector.getBeanInfo(...)

所以,它原则需要符合javaBean规范的属性拷贝。

看到我这么说,是不是感觉有漏洞,再怎么反省,它拿的还是get set方法,只要源类有get方法,目标类有set方法,就没问题,属性名首字母大写也没问题。

最后一点,属性名相同,但是类型不同的,怎么办?

我们看源码:

if (useConverter) {

...

} else if (compatible(getter, setter)) {

e.dup2();

e.invoke(read);

e.invoke(write);

}

private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {

// TODO: allow automatic widening conversions? return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());

}

只有在compatible返回true的时候,才能拷贝值,不然就只能通过转换器Converter了,这里说一下,如果使用了转换器,所有的属性都会走转换器,所以转换器里需要考虑所有类型的情况,这里就不做演示了。

总结

我个人非常喜欢用cglib里的包,cglib的BeanCopier效率也是非常高的,推荐使用。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。