4

我的想法用完了,谷歌也没有帮助。用例似乎微不足道,但因 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。

这种行为的原因可能是什么?你有什么主意吗?

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

添加以满足马里奥的要求

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

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

再次编辑以提供更多信息。 我的一个朋友查看了代码,并对这个问题提出了新的看法。

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

// 给定

有一个应用程序分为两部分,第一个基础应用程序(保存模型文件)和 Web 应用程序(保存 UI 连接文件,如 backing beans 等)。 我们的类别和代码是模型类,因此位于基础应用程序中。然后我们有一个支持 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 提供的。应用程序在网络轮胎中执行,因此对 Callback.class 的调用将通过网络应用程序进行服务器。类加载器和返回的 Callback.class 将与之前作为提到的setThreadsCallbacks函数的参数放置的内容不同。反思残酷地失败了。

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

这就是为什么我无法提供失败的测试。(相同的类加载器)。

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

4

1 回答 1

1

我遇到了类似的问题。(同样抛出异常:java.lang.ClassCastException: XXX$Category$$EnhancerByCGLIB$$XXX cannot be cast to net.sf.cglib.proxy.Factory)

就我而言,结果证明是重复(甚至三倍)CGLib 库的问题。我们有如下:

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

然后,当使用 Lambda.on(SomeClass.class) 时,我们会在 CGLib 的Enhancer类中使用此方法:

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

wheremethodName = "CGLIB$SET_THREAD_CALLBACKS" 和 type 是我们的 SomeClass 由Enhancer.

“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 类都是从共同的地方加载的,问题已经消失了。

于 2013-02-04T12:17:13.220 回答