我想在 Java 中将一个 bean 复制到另一个 bean。问题是它位于类加载杂耍框架内,并且两个类由不同的类加载器加载,这也与当前的类加载器不同。
需要以最有效的方式完成此操作,因此最好的方法是使用字节码生成解决方案(如 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):
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.* 中的特权和类(因此它们由标准类加载器加载)。
最佳答案
这听起来像是 cglib 中存在一个错误,它只考虑类名而不查看类加载器。事实上,当您不使用使用较多的Enhancer时,cglib充满了错误,而BeanCopier则相当奇特。还有更多坏消息,cglib 的开发不是很活跃,所以修复这个 bug 最好的办法就是自己尝试。
您可以尝试的是将 Converter
添加为 copy
的第三个参数,并将第三个构造函数参数更改为 true
以激活此转换器。让 Converter
仅返回 value
参数。此解决方案的一个缺点是,每次复制值时,您的基元都会被装箱和取消装箱,这对性能来说非常糟糕。
不过,有个好消息。现代 JVM(例如 HotSpot)知道一个称为通货膨胀的概念。当多次应用特定反射(在当前 HotSpot JVM 上超过 15 次)时,就会应用膨胀。然后,JVM 创建与该反射调用相对应的字节代码,并用生成的字节代码替换它。这正是 BeanCopier 所做的,但有错误(显然)。因此,BeanCopier
已经是旧闻了。现代 JVM 足够聪明,可以自行应用这种优化。如果您想了解更多有关通货膨胀的信息,例如可以咨询HotSpot related javadoc .
为了进一步阅读,我发现 this article您可能会觉得有趣。
关于java - 将 cglib BeanCopier 与多个类加载器一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20816197/