61

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

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

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

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

portletRequest.setAttribute("DynamicBean2", bean);

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

当我使用将对象拉出会话时

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

并使用.getClass()I get来检查它的类别dynamicbeans.DynamicBean2。这是我想将其转换为的类,但是当我尝试得到 ClassCastException 时。

我得到这个有什么理由吗?

4

11 回答 11

65

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

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

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

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

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

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

于 2009-05-05T18:46:27.470 回答
21

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

参考:Spring boot devtools相关的一个dozer map异常

于 2019-03-12T22:32:55.737 回答
14

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

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

于 2009-05-05T18:47:34.970 回答
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()),但在这种情况下,这感觉是一个非常丑陋的解决方案。

于 2011-11-25T15:21:08.633 回答
1

在 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>

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

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

为此,在那些WAR的源代码中,需要将<scope>provided</scope>这些jar添加到.pom.xmlmy.warmy.war/WEB-INF/lib

于 2018-08-10T10:00:04.143 回答
0

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

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

编辑:我编辑了 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

于 2012-02-23T12:40:24.387 回答
0

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

将所有org.eclipse.persistence jar文件复制glassfish4/glassfish/modulesWEB-INF/lib. 然后进入你的glassfish-web.xml,并设置class-delegatefalse

为我工作!

于 2014-11-17T13:27:13.300 回答
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

于 2017-01-07T21:23:18.917 回答
0

我在 Wildfly EJB 上遇到了同样的问题,EJB 返回了一个对象列表,并且有一个远程和一个本地接口。我错误地使用了本地界面,直到您尝试将对象投射到列表中为止。

本地/远程接口:

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

于 2017-02-21T08:25:13.003 回答
0

另外一个选项:

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

于 2017-02-28T11:22:04.323 回答
0

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

于 2017-06-21T15:16:06.760 回答