简单选择lambdaJ和ClassCastException

时间:2013-01-21 13:35:40

标签: java classloader classcastexception cglib lambdaj

我用尽了想法,谷歌也没有帮助。用例似乎微不足道,但它因ClassCastException而失败。我不知道我做错了什么。 有一个简单的方法可以返回匹配给定类别的第一个元素,看看。

private Category selectElement(List<? extends Category> results, Code code) {
    return selectFirst(results, having(on(Category.class).getCode(), is(code)));
}

执行给出了这个堆栈顶部:

java.lang.ClassCastException: name.wilu.logic.report.utils.SheetLoader$Category$$EnhancerByCGLIB$$3a35aefc cannot be cast to net.sf.cglib.proxy.Factory
at ch.lambdaj.proxy.ClassImposterizer.createProxy(ClassImposterizer.java:134)
at ch.lambdaj.proxy.ClassImposterizer.imposterise(ClassImposterizer.java:101)
at ch.lambdaj.proxy.ProxyUtil.createProxy(ProxyUtil.java:52)
at ch.lambdaj.function.argument.ArgumentsFactory.createPlaceholder(ArgumentsFactory.java:68)
at ch.lambdaj.function.argument.ArgumentsFactory.registerNewArgument(ArgumentsFactory.java:58)
at ch.lambdaj.function.argument.ArgumentsFactory.createArgument(ArgumentsFactory.java:50)
at ch.lambdaj.function.argument.ArgumentsFactory.createArgument(ArgumentsFactory.java:39)
at ch.lambdaj.Lambda.on(Lambda.java:63)

在使用lambdaJ进行操作而不是hibernate的持久化集合持有实体时,我遇到了同样的问题。假设代理对象(集合中的实体)已经是代理可能存在一些问题,我放弃了。 看来我错了,因为类和所有继承的类都是pojos传递给hibernate作为结果变换器。

这种行为可能是什么原因? 你有什么想法吗?

(我正在使用最新的lambdaj-2.4)。

已添加以满足Mario的要求

代码是一个简单的枚举。 Category是不同类别的基类,它有代码字段。此外,它是公共静态类,与所有继承类相同(如果重要)。

我会尝试提供失败的测试。

再次编辑以提供其他信息。 我的一个朋友看了看代码,并对这个问题提出了新的亮点。

我会尝试从一开始就重现我们的演绎路径。

//给定

有一个应用程序分为两个部分,第一个 - 基础应用程序(保留模型文件)和Web应用程序(保持UI连接文件,如支持bean等)。 我们的类别和代码是模型类,因此位于基础应用程序中。我们有一个支持bean用于某些Web逻辑,特别是该bean或其协作者调用我们的select。 /强>

//

我们正在将应用程序部署到Web服务器! JBoss在我的情况下。类是由加载器读取的,一些我不知道发生的非常复杂的事情,都是让我的应用程序运行。 我做了一些网络动作,并且调用了支持bean的方法

 selectFirst(results, having(on(Category.class).getCode(), is(code)));

来自应用程序的Web部分。

这就是魔术。 我们的Category.class和Code.class由UnifiedClassLoader在应用程序加载时加载。 我们在 on(Category.class)方法中,并且将构建Category的代理。一些真正纠结的逻辑被用来做到这一点,最重要的是,代理用

进行检测
setThreadsCallbacks(Callback[]callbacks) 

方法,但Callback.class取自该类加载器

 aCategory.getClass.getClassLoader()

因此它是一个最初加载该类的类加载器,即UnifiedClassLoader。 干净利落地完成所有这些工作后我们终于打电话了

 getFirstInstance()

使用反射浏览代理类寻求:      Proxy.getDeclaredMethod(“setThreadsCallbacks”,new Class [] {Callback [] .class});

我省略了这个事实,我不明白

 new Class[]{ Callback[].class }

在我们的案例中,重要的是Callback.class不是由UnifiedClassLoader提供的。 Applicaiton在Web轮胎中执行,因此Callback.class的调用将由Web应用程序服务。类加载器和重新调用的Callback.class将从以前作为提到的 setThreadsCallbacks 功能的段中提供。反思失败了。

Category.class != Category.class //these two were provided by different classLoaders

这就是我无法提供失败测试的原因。 (同一类加载器)。

我怀疑这种情况有什么解决方案。

1 个答案:

答案 0 :(得分:1)

我遇到过类似的问题。 (抛出相同的异常:java.lang.ClassCastException:XXX $ Category $$ EnhancerByCGLIB $$ XXX无法强制转换为net.sf.cglib.proxy.Factory)

在我的情况下,结果是重复(甚至三倍)CGLib库存在问题。我们有如下:

  • cglib.jar在JBoss(4.2.3)库中
  • 我们的应用程序web-library(WAR文件)中的
  • cglib-nodep.jar
  • labmdaj-2.4-with-dependencies.jar in application web-library。

然后,当使用Lambda.on(SomeClass.class)时,我们在CGLib的Enhancer类中找到了这个方法:

private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
        return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class });
}

其中methodName = "CGLIB$SET_THREAD_CALLBACKS" 和type是由Enhancer包装的SomeClass。

“CGLIB $ SET_THREAD_CALLBACKS”方法存在于包装类型中,但getDeclaredMethod()返回null。似乎在getDeclaredMethod()里面有两个net.sf.cglib.proxy.Callback.class实例的比较。它们是不同的,因为一个从cglib.jar(JBoss)加载,另一个从cglib-nodep.jar(webapp)加载。

解决方案是删除冗余的cglib-nodep.jar并将lambda-2.4-with-dependencies.jar替换为lambda-2.4.jar。现在所有的CGLib类都是从普通的地方加载的,问题已经消失了。