转换为同一个类时出现ClassCastException

时间:2009-05-05 18:38:14

标签: java reflection classcastexception websphere-portal

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

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

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

然后我继续使用Validator创建一个validatorClass.newInstance()对象并将其存储在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时。

为什么我得到这个?

11 个答案:

答案 0 :(得分:56)

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

有一个article about classloading in WebSphere。我不能说它如何适用于您的应用程序,但有许多可能的解决方案。我至少可以想到:

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

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

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

  4. 但是,对于您的特定情况,可能有更合适的方式。

答案 1 :(得分:12)

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

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

答案 2 :(得分:2)

尝试使用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
}

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

答案 3 :(得分:0)

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

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

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

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

您可以在此处找到一些信息:https://community.jboss.org/wiki/ClassLoadingConfiguration

答案 4 :(得分:0)

我有同样的问题,我终于找到了一个关于java.net的解决方法:

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

为我工作!

答案 5 :(得分:0)

我遇到了与JAXB和JBoss AS 7.1类似的问题。此处描述了问题和解决方案:javax.xml.bind.JAXBException: Class *** nor any of its super class is known to this context。给出的异常是org.foo.bar.ValueSet无法强制转换为org.foo.bar.ValueSet

答案 6 :(得分:0)

我在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周围正确的spring包装器:

<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接口就好了,并且执行方法没有任何问题(来自同一容器上的单独应用程序),但模型对象不是如果您错误地使用本地接口,则封送并来自不同的类加载器。

答案 7 :(得分:0)

另一种选择:

在weblogic中发生了我,但我想它也可能发生在其他服务器上 - 如果你这样做(只是)&#34;发布&#34;因此,你的一些课程被重新加载。而是&#34;清洁&#34;所以所有的课程都会重新加载。

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

我已将{strong>模块(其中包含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 的来源中,需要为<scope>provided</scope>中的这些 jar 添加pom.xml,以便在下次将my.war重新组装并注入修复/增强代码时,它不会将此 jar 捆绑到my.war/WEB-INF/lib中。

答案 10 :(得分:0)

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

参考:A dozer map exception related to Spring boot devtools