繁体   English   中英

强制转换到同一类时出现 ClassCastException

[英]ClassCastException when casting to the same class

我有2个不同的Java项目,一个有2类: dynamicbeans.DynamicBean2dynamic.Validator

在另一个项目中,我动态加载这两个类并将它们存储在一个Object

class Form {
    Class beanClass;
    Class validatorClass;
    Validator validator;
}

然后我继续使用validatorClass.newInstance()创建一个Validator对象并将其存储在validator然后我也使用beanClass.newInstance()创建一个 bean 对象并将其添加到会话中。

portletRequest.setAttribute("DynamicBean2", bean);

Form项目的生命周期中,我调用了validator.validate() ,它从会话中加载先前创建的 bean 对象(我正在运行 Websphere Portal Server)。 当我尝试将此对象转换回DynamicBean2它失败并显示 ClassCastException。

当我使用

faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces);

并使用.getClass()检查它的类我得到dynamicbeans.DynamicBean2 这是我想将它投射到的类,但是当我尝试时,我得到了 ClassCastException。

我得到这个的任何原因?

我不太了解您对程序流程的描述,但通常当您收到 ClassCastExceptions 时,您无法解释您已使用一个类加载器加载了该类,然后尝试将其转换为由另一个类加载器加载的同一个类。 这将不起作用 - 它们由 JVM 内的两个不同的 Class 对象表示,并且转换将失败。

一篇关于 WebSphere 中的类加载文章 我不能说它如何适用于您的应用程序,但有许多可能的解决方案。 我至少能想到:

  1. 手动更改上下文类加载器。 要求您实际上可以获得对适当类加载器的引用,这在您的情况下可能是不可能的。

     Thread.currentThread().setContextClassLoader(...);
  2. 确保该类由层次结构中更高的类加载器加载。

  3. 序列化和反序列化对象。 (呸!)

不过,对于您的特定情况,可能有更合适的方法。

在我的 Springboot 项目中添加对spring-boot-devtools的依赖后,我遇到了这个问题。 我删除了依赖项,问题就消失了。 在这一点上,我最好的猜测是spring-boot-devtools了一个新的类加载器,并且在某些线程未使用新类加载器的某些情况下,这会导致不同类加载器之间的类转换问题。

参考: 一个与Spring boot devtools相关的推土机地图异常

类对象在不同的​​类加载器中加载,因此在每个类中创建的实例被视为“不兼容”。 在使用许多不同的类加载器并且正在传递对象的环境中,这是一个常见问题。 这些问题很容易在 Java EE 和门户环境中出现。

转换类的实例要求链接到被转换对象的类与当前线程上下文类加载器加载的类相同。

我在尝试使用 Apache Commons Digester 从 XML 创建对象列表时遇到了 A2AClassCastException 问题。

List<MyTemplate> templates = new ArrayList<MyTemplate>();
Digester digester = new Digester();
digester.addObjectCreate("/path/to/template", MyTemplate.class);
digester.addSetNext("/path/to/template", "add");
// Set more rules...
digester.parse(f); // f is a pre-defined File

for(MyTemplate t : templates) { // ClassCastException: Cannot cast mypackage.MyTemplate to mypackage.MyTemplate
    // Do stuff
}

如上所述,原因是摘要器没有使用与程序其余部分相同的类加载器。 我在 JBoss 中运行了这个,结果发现 commons-digester.jar 不在 JBoss 的 lib 目录中,而是在 webapp 的 lib 目录中。 将 jar 复制到 mywebapp/WEB-INF/lib 也解决了问题。 另一种解决方案是调用digester.setClassLoader(MyTemplate.class.getClassLoader()),但在这种情况下,这感觉是一个非常丑陋的解决方案。

在 WildFly 10.1 上my.package.MyClass cannot be cast to my.package.MyClass相同的my.package.MyClass cannot be cast to my.package.MyClass ,据我所知,我做了与@Emil Lundberg 在他的回答中描述的相反的事情。

我已将模块(包含my.package.MyClass )添加到my.war/WEB-INF/jboss-deployment-structure.xml作为依赖项

<dependencies>
    ...
    <module name="my.package"/>
</dependencies>

并从my.war/WEB-INF/lib删除相应的jar ,重新部署 WAR,然后代码按预期工作。

因此,我们确保它解决了这个问题。 现在,我们需要确保问题不会再次出现,例如,何时组装和部署更新版本的WAR

为此,在那些WAR的源代码中,需要在pom.xml为那些jar添加<scope>provided</scope> ,以便下次重新组装my.war时注入修复/增强代码,它不会将此jar捆绑到my.war/WEB-INF/lib

我遇到了同样的问题,我终于在 java.net 上找到了解决方法:

将所有org.eclipse.persistence jar文件从glassfish4/glassfish/modules复制到WEB-INF/lib 然后进入你的glassfish-web.xml ,并将class-delegate设置为false

为我工作!

我在 JAXB 和 JBoss AS 7.1 上遇到了类似的问题。 此处描述了问题和解决方案: javax.xml.bind.JAXBException: Class *** 或其任何超类都不为该上下文所知 给出的例外是 org.foo.bar.ValueSet 不能转换为 org.foo.bar.ValueSet

我在 Wildfly EJB 上遇到了同样的问题,EJB 正在返回一个对象列表,并且有一个远程和一个本地接口。 我错误地使用了 Local 接口,直到您尝试将对象转换为列表中的对象之前,它一直运行良好。

本地/远程接口:

public interface DocumentStoreService {

    @javax.ejb.Remote
    interface Remote extends DocumentStoreService {
    }

    @javax.ejb.Local
    interface Local extends DocumentStoreService {
    }

EJB bean:

@Stateless
public class DocumentStoreServiceImpl implements DocumentStoreService.Local, DocumentStoreService.Remote {

EJB 周围正确的弹簧包装器:

<bean id="documentStoreService" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="java:global/dpc/dpc-ejb/DocumentStoreServiceImpl!santam.apps.dpc.service.DocumentStoreService$Remote"/>
    <property name="businessInterface" value="santam.apps.dpc.service.DocumentStoreService$Remote"/>
    <property name="resourceRef" value="true" />
</bean>

请注意 $Remote,您可以将其更改为 $Local,它会发现 Local 接口很好,并且还可以毫无问题地执行方法(来自同一容器上的单独应用程序),但模型对象未编组,而是如果您错误地使用本地接口,则从不同的类加载器。

另外一个选项:

在 weblogic 中发生在我身上,但我想它也可能发生在其他服务器上 - 如果你(只是)“发布”并且因此你的一些类被重新加载。 而是执行“清理”,以便所有类将重新加载在一起。

我在从另一个 EJB 查找 EJB 时遇到了同样的问题。 我解决了将@Remote(MyInterface.class) 添加到 EJB 类配置的问题

我在不同的机器上使用多个 JBoss 实例时遇到了同样的问题。 糟糕的是,我之前没有偶然发现这篇文章。
在不同的机器上部署了工件,其中两个声明了具有相同名称的类加载器。我更改了一个类加载器名称,一切正常 => 小心复制和粘贴!

为什么抛出的 ClassCastException 没有提到所涉及的类加载器? - 我认为这将是非常有用的信息。
有谁知道将来是否会有这样的东西可用? 需要检查 20-30 个工件的类加载器并不是那么愉快。 或者我在异常文本中遗漏了什么?

编辑:我编辑了 META-INF/jboss-app.xml 文件并更改了加载程序的名称,想法是要有一个唯一的名称。 在工作中,我们将工件 id(unique) 与构建期间由 maven({$version}) 插入的版本结合使用。
使用动态字段只是可选的,但如果您想部署同一应用程序的不同版本,则会有所帮助。

<jboss-app>
   <loader-repository> 
   com.example:archive=unique-archive-name-{$version}
   </loader-repository> 
</jboss-app>

你可以在这里找到一些信息: https : //community.jboss.org/wiki/ClassLoadingConfiguration

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM