1

I'd like to copy one bean to another in Java. The problem that it is inside classloading-juggler framework and both classes are loaded by different classloaders which also differs from the current classloader.

It is required to do it in most efficient way, so the best way is to use bytecode generation solutions (like cglib BeanCopier), but not reflection-based.

The issue is that cglib BeanCopier doesn't work in this case. The simplest code to show it is:

URL classesUrl = new File("/directory/with/class-files-for-Person").toURL();
Class<?> c1 = new URLClassLoader(new URL[] {classesUrl}).loadClass("Person");
Class<?> c2 = new URLClassLoader(new URL[] {classesUrl}).loadClass("Person");
Object o1 = c1.newInstance();
Object o2 = c2.newInstance();
BeanCopier copier = BeanCopier.create(o1.getClass(), o2.getClass(), false);
copier.copy(o1, o2, null);

It gives the exception:

Exception in thread "main" java.lang.ClassCastException: Person cannot be cast to Person
at net.sf.cglib.empty.Object$$BeanCopierByCGLIB$$da60538e.copy(<generated>)
at Main.main(Main.java:22)

I'm finding the way to resolve it. In my case both classes are the same but loaded with custom classloaders. Also the properties contain only privimives and classes from java.* (so they are loaded by standard classloader).

4

1 回答 1

1

这听起来像是 cglib 中的一个错误,它只考虑类名但不查看类加载器。事实上,当你不使用时,cglib 充满了错误,Enhancer其中使用得更多,而BeanCopier则相当奇特。还有更多的坏消息,cglib 的开发不是很活跃,所以你能做的最好的修复错误就是尝试自己。

您可以尝试添加 aConverter作为第三个参数copy并将第三个构造函数参数更改true为以激活此转换器。让Converter简单地返回value参数。这个解决方案的一个缺点是,每次复制一个值时,您的基元都会被装箱和拆箱,这对性能来说很糟糕。

不过,也有好消息。诸如 HotSpot 之类的现代 JVM 知道一个称为通货膨胀的概念。当多次应用特定反射(在当前 HotSpot JVM 上超过 15 次)时,会应用膨胀。然后,JVM 创建与此反射调用对应的字节码,并将其替换为生成的字节码。这正是它所做的BeanCopier,但有错误(显然)。出于这个原因,这BeanCopier是旧消息。现代 JVM 足够聪明,可以自己应用这种优化。如果您想了解更多关于通货膨胀的信息,例如可以参考HotSpot 相关的 javadoc

为了进一步阅读,我发现这篇文章你可能会觉得有趣。

于 2013-12-29T00:26:05.660 回答