Java 无法通过类型强制转换为类型
当我想将一种类型转换为另一种类型时,会出现以下异常Java 无法通过类型强制转换为类型,java,tomcat,classloader,Java,Tomcat,Classloader,当我想将一种类型转换为另一种类型时,会出现以下异常 java.lang.ClassCastException: org.paston.certification.data.impl.BRL6000 cannot be cast to org.paston.certification.data.Certification BRL6000扩展了认证。因此,在我的理解中,我应该能够将BRL6000类型的对象强制转换为认证类型 这是发生异常的代码 Object certification = ch.
java.lang.ClassCastException: org.paston.certification.data.impl.BRL6000
cannot be cast to org.paston.certification.data.Certification
BRL6000扩展了认证。因此,在我的理解中,我应该能够将BRL6000类型的对象强制转换为认证类型
这是发生异常的代码
Object certification = ch.getCertificationData(process, version);
Certification c = (Certification)certification;
部署
应用程序从Eclipse部署到Tomcat7服务器。我的应用程序使用了Tomcat环境中的一些jar(例如,Bonita_Server.jar)
我的应用程序(在Eclipse中)是一个动态web项目,它引用了另一个项目(Certificationnl),该项目包含类Certification
和BRL6000
。当我将应用程序部署到Tomcat时,Project Certificationnl被添加到webproject的WAR中
课程
BRL6000类
package org.paston.certification.data.impl;
import org.paston.certification.data.Certification;
import org.paston.certification.data.CertificationStep;
public class BRL6000 extends Certification{
/**
*
*/
public static final long serialVersionUID = -8215555386637513536L;
public static final String processName = "BRL6000";
}
认证班
package org.paston.certification.data;
import java.util.ArrayList;
import java.util.List;
import org.ow2.bonita.facade.runtime.impl.AttachmentInstanceImpl;
public class Certification implements java.io.Serializable{
public enum Section{
ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN
}
/**
* SerializationVersionUID
*/
private static final long serialVersionUID = -5158236308772958478L;
}
getCertificationData
public Object getCertificationData(String process, String version) {
if (loginContext == null)
login();
System.out.println("Process: "+ process + " Version: "+ version);
ProcessDefinitionUUID pdu = new ProcessDefinitionUUID(process, version);
QueryRuntimeAPI queryRuntimeAPI = AccessorUtil
.getQueryRuntimeAPI();
try {
Set<ProcessInstance> processInstances = queryRuntimeAPI
.getProcessInstances(pdu);
if (processInstances.size() != 1)
System.out.println("Best number of instances is 1. Found: "
+ processInstances.size());
for (ProcessInstance processInstance : processInstances) {
Map<String, Object> variables = processInstance
.getLastKnownVariableValues();
if (((Boolean) variables.get("active")) == true) {
return variables.get("certification");
}
}
} catch (ProcessNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
更新2
我对这个问题有了更好的理解。Bonitaserver(AccessorUtil
)也会加载认证对象。它从Certification.jar1620768823629427276.tmp
加载类,该类是Bonitaserver在将进程上载到服务器时创建的
此外,我还发现了一个类reflectil
(),它可能用于加载这些类
我尝试的是在doGet的开头为这两个(servlet)加载类,作为AccessorUtil
的ClassLoader
。两者都有相同的旧结果
ArrayList<String> classesNames = new ArrayList<String>();
classesNames.add("org.paston.certification.data.Certification");
classesNames.add("org.paston.certification.data.CertificationI");
classesNames.add("org.paston.certification.data.impl.BRL6000");
ClassLoader cl = this.getClass().getClassLoader();
Class<?>[] classes = ReflectUtil.loadClasses(cl, classesNames);
守则的结果:
--- Test ClassLoader certification object---
org.ow2.bonita.runtime.ProcessClassLoader 451656
org.ow2.bonita.runtime.ProcessClassLoader@6e448
org.ow2.bonita.runtime.VirtualCommonClassloader 1182018350
org.ow2.bonita.runtime.VirtualCommonClassloader@46742b2e
org.apache.catalina.loader.StandardClassLoader 318536939
org.apache.catalina.loader.StandardClassLoader@12fc7ceb
sun.misc.Launcher.AppClassLoader 1667514408
sun.misc.Launcher$AppClassLoader@63644028
sun.misc.Launcher.ExtClassLoader 1253061906
sun.misc.Launcher$ExtClassLoader@4ab03512
--- Test ClassLoader Certification class---
org.apache.catalina.loader.WebappClassLoader 2136824254
WebappClassLoader context: /Certification delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader: org.apache.catalina.loader.StandardClassLoader@12fc7ceb
org.apache.catalina.loader.StandardClassLoader 318536939
org.apache.catalina.loader.StandardClassLoader@12fc7ceb
sun.misc.Launcher.AppClassLoader 1667514408
sun.misc.Launcher$AppClassLoader@63644028
sun.misc.Launcher.ExtClassLoader 1253061906
sun.misc.Launcher$ExtClassLoader@4ab03512
我怀疑问题的根源在于运行时环境 您的
认证
数据最终来自AccessorUtil.getQueryRuntimeAPI()
,这是一个由Tomcat运行的静态方法,因此来自它的所有对象实例都可能由Tomcat类加载器加载
如果在Tomcat下复制jar文件,它将使用自己的类加载器加载它。尽管eclipse运行代码使用不同的类加载器,但如果它们没有加载此认证
类的公共祖先类加载器,它们将被视为不同的类
我建议查看运行时类路径,从Tomcat中删除lib,或者(最坏的情况)在Tomcat中运行代码(例如,作为同一应用程序中的Servlet)
更新:
基于项目部署描述,我认为
类由公共类加载器加载AccessorUtil
- 里面的列表由Tomcat部署中的
类中的Certificationnl
实例填充Certification
- 您可以通过
类访问这些对象,但是不能从Eclipse代码访问类定义AccessorUtil
AccessorUtil
旁边)并强制转换到接口的接口
更新2:
要检查类加载器层次结构,请执行如下代码:
Class c1 = certification.getClass().getSuperclass().getClassLoader();
while (c1 != null) {
System.out.println(c1.getClass().getCanonicalName() + " " + c1.hashCode() + " " + c1);
c1 = c1.getParent();
}
Class c2 = Certification.class.getClassLoader();
while (c2 != null) {
System.out.println(c2.getClass().getCanonicalName() + " " + c2.hashCode() + " " + c2);
c2 = c2.getParent();
}
您可以通过比较堆栈来找到共同的祖先。最好的方法仍然是调试
更新3:
可见,您的通用类加载器是org.apache.catalina.loader.StandardClassLoader
。除此之外
已为您的webapp提供(这是您希望转换为的地方)org.apache.catalina.loader.WebappClassLoader
和org.ow2.bonita.runtime.VirtualCommonClassloader
是对象的来源(即实例化对象的)org.ow2.bonita.runtime.ProcessClassLoader
标准类加载器
(或任何其他类加载器)来加载类。由于Certification
(因此BRL6000
)依赖于Bonita类,因此您必须将Bonita_Server.jar
放入已认可的。请参阅,它可能会给您更多的洞察力
更新4:
要执行我的评论中建议的序列化/反序列化,可以使用以下代码:
Certification certification = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ch.getCertificationData(process, version));
oos.flush();
certification = (Certification) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject();
} catch (IOException | ClassNotFoundException ex) {
}
请注意,(认证)
转换是对当前加载的类进行的
更新5:
最终的解决方案是不使用直接Java类级访问,而是使用适当的API来执行相同的操作 我认为有两种可能的解释:
- 您已经设法在两个不同的类加载器中加载了
类认证
- 您有两个
类,它们的名称看起来完全相同Certification
- 在进行
(认证)认证的语句之前插入以下内容:
// Get the classes that are being compared in the typecast. Class c1 = certification.getClass().getSuperclass(); Class c2 = Certification.class; System.out.println("c1 is " + c1 + ", c2 is " + c2); System.out.println("c1 == c2 is " + c1 == c2); System.out.println("c1.equals(c2) is " + c1 == c1.equals(c2)); System.out.println("c1.getName().equals(c2.getName()) is " + c1.getName().equals(c2.getName())); System.out.println("c1.getClassLoader() == c2.getClassLoader() is " + c1.getClassLoader() == c2.getClassLoader());
- 检查第一行给出的
和c1
的名称是否相同。(如果没有,我们选择了错误的类进行比较。请调整我的代码以获得正确的类。)c2
和c1==c2
测试应该给出相同的答案,我预测它将是c2.equals(c2)
false
- 比较名称是区分两种不同解释的测试:
- 如果名称相等,则表示类加载器出现问题
- 如果名称不相等,则有两个不同的类,它们的名称看起来相同,但实际上不同。(怎么可能呢?Java使用Unicode,还有一些
Class c1 = certification.getClass().getSuperclass().getClassLoader(); while (c1 != null) { System.out.println(c1.getClass().getCanonicalName() + " " + c1.hashCode() + " " + c1); c1 = c1.getParent(); } Class c2 = Certification.class.getClassLoader(); while (c2 != null) { System.out.println(c2.getClass().getCanonicalName() + " " + c2.hashCode() + " " + c2); c2 = c2.getParent(); }
Certification certification = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(ch.getCertificationData(process, version)); oos.flush(); certification = (Certification) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject(); } catch (IOException | ClassNotFoundException ex) { }
// Get the classes that are being compared in the typecast. Class c1 = certification.getClass().getSuperclass(); Class c2 = Certification.class; System.out.println("c1 is " + c1 + ", c2 is " + c2); System.out.println("c1 == c2 is " + c1 == c2); System.out.println("c1.equals(c2) is " + c1 == c1.equals(c2)); System.out.println("c1.getName().equals(c2.getName()) is " + c1.getName().equals(c2.getName())); System.out.println("c1.getClassLoader() == c2.getClassLoader() is " + c1.getClassLoader() == c2.getClassLoader());