转换为同一个类时出现ClassCastException

问题描述 投票:50回答:11

我有两个不同的Java项目,一个有2个类: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时。

为什么我得到这个?

java reflection classcastexception websphere-portal
11个回答
57
投票

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

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

  1. 手动更改上下文类加载器。要求您实际上可以获得对适当的类加载器的引用,这在您的情况下可能是不可能的。 Thread.currentThread().setContextClassLoader(...);
  2. 确保该类由层次结构中较高的类加载器加载。
  3. 序列化和反序列化对象。 (呸!)

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


0
投票

在WildFly 10.1上有相同的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的来源中,需要在<scope>provided</scope>中为那些jar添加pom.xml,这样当下次使用注入的修复/增强代码重新组装my.war时,它不会将此jar包装到my.war/WEB-INF/lib中。


0
投票

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

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


12
投票

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

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


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()),但在这种情况下,这感觉就像一个非常难看的解决方案。


0
投票

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

为什么抛出的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


0
投票

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

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

为我工作!


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


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接口很好,并且也可以毫无问题地执行这些方法(来自同一容器上的单独应用程序),但是模型对象没有被编组,而且是如果您错误地使用本地接口,则从不同的类加载器。


0
投票

另外一个选项:

在weblogic中发生了我,但我想它也可能发生在其他服务器上 - 如果你这样做(只是)“发布”,那么你的一些类会被重新加载。而是“清理”所以所有类将重新加载在一起。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.