将cglib BeanCopier与多个类加载器一起使用

时间:2013-12-28 15:29:46

标签: java javabeans cglib dynamic-class-loaders

我想在Java中将一个bean复制到另一个bean。它在classloading-juggler框架中的问题和两个类都由不同的类加载器加载,这也与当前的类加载器不同。

需要以最有效的方式完成,因此最好的方法是使用字节码生成解决方案(如cglib BeanCopier),但不能使用基于反射的方法。

问题是cglib BeanCopier在这种情况下不起作用。显示它的最简单的代码是:

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);

它给出了例外:

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)

我找到了解决问题的方法。在我的例子中,两个类都相同,但加载了自定义类加载器。此外,属性仅包含来自java。*的私有化和类(因此它们由标准类加载器加载)。

1 个答案:

答案 0 :(得分:1)

这听起来像cglib中有一个错误,它只考虑类名但不看类加载器。事实上,当你没有使用Enhancer时,cglib充满了bug,BeanCopier更具异国情调。还有更多的坏消息,cglib不是很积极开发,所以你可以做的最好的办法就是自己尝试修复bug。

您可以尝试将Converter添加为copy的第三个参数,并将第三个构造函数参数更改为true以激活此转换器。让Converter只返回value参数。这个解决方案的缺点是,每次复制值时,您的基元将被装箱和取消装箱,这对于性能来说是非常糟糕的。

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

为了进一步阅读,我发现this article您可能会感兴趣。