java ClassLoader问题导致ClassCastException的解决方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7074532/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 18:32:49  来源:igfitidea点击:

Solution for the ClassCastException due to ClassLoader issue

javaclassloaderclasscastexception

提问by ravana

I have two ClassLoaders which loads the same class. So, obviously these can't cast to one another. But I need to access an object created in the other ClassLoader.

我有两个加载同一个类的类加载器。所以,显然这些不能相互转换。但是我需要访问在另一个 ClassLoader 中创建的对象。

I have access to both ClassLoaders. How can I use that object in the other class? I don't need to cast the object to match to the current ClassLoader.

我可以访问两个类加载器。如何在另一个类中使用该对象?我不需要强制转换对象以匹配当前的 ClassLoader。

But the issue is that the returned object's type is Object. So, I have to cast down that object to access some methods. How can I do that? Normal cast like the following causes ClassCastException, which I already know.

但问题是返回对象的类型是Object. 因此,我必须放弃该对象才能访问某些方法。我怎样才能做到这一点?像下面这样的正常转换会导致 ClassCastException,我已经知道了。

Mojo mojo = (Mojo) descriptor.getMojo();

descriptor#getMojo()returns an object of type Mojobut the method returns Object. How can do this?

descriptor#getMojo()返回一个类型的对象,Mojo但该方法返回Object. 怎么能做到这一点?

Let me know if you need further info.

如果您需要更多信息,请告诉我。

I've read all the theories about classloading, but none has specified a proper solution for this.

我已经阅读了所有关于类加载的理论,但没有人为此指定适当的解决方案。

回答by Sanjay T. Sharma

AFAIK, no, you can't cast an object of a class loaded by one class-loader in another class loader.

AFAIK,不,您不能在另一个类加载器中转换由一个类加载器加载的类的对象。

  • One solution would be to create a "common" class-loader which loads the classes to be used by your custom classloaders. So in your case, you'd have a new classloader which would load the given class and your custom classloaders would extend this classloader.
  • Another solution would be to pass around the "serialized" state between the two classloaders. Serialize one instance to a byte array and reconstruct the object in the other classloader by de-serializing the object stream.
  • 一种解决方案是创建一个“通用”类加载器,用于加载自定义类加载器要使用的类。所以在你的情况下,你会有一个新的类加载器来加载给定的类,而你的自定义类加载器将扩展这个类加载器。
  • 另一种解决方案是在两个类加载器之间传递“序列化”状态。将一个实例序列化为字节数组,并通过反序列化对象流在另一个类加载器中重建对象。

回答by Ed Staub

Reflection isn't that bad, and is appropriate here.
Is this a Maven plugin, BTW?

反射并没有那么糟糕,在这里很合适。
顺便说一句,这是一个Maven插件吗?

You'll want something like:

你会想要这样的:

Mojo mojo = (Mojo)descriptor.getClass().getMethod("getMojo").invoke(descriptor);

I'm leaving out a lot - particularly exception handling - but this should lead you to the Javadoc you need. It's quite good, but read carefully.

我遗漏了很多 - 特别是异常处理 - 但这应该会引导您找到所需的 Javadoc。写的很好,但请仔细阅读。

If you also have two Mojo classes, the cast will break, and you'll have to do more reflection to do whatever you need to do with the evil-twin Mojo.

如果您还有两个 Mojo 类,则演员表会中断,您必须进行更多反思才能对邪恶双胞胎 Mojo 做任何需要做的事情。

回答by user3155799

I think better option to just store byte array instead of object. While deserliazing, get byte array back and convert into object.

我认为更好的选择是只存储字节数组而不是对象。在反串时,取回字节数组并转换为对象。

I had the same issue and byte array approach worked.

我有同样的问题,字节数组方法有效。

ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutput out = null;
    try {
        out = new ObjectOutputStream(bos);
        out.writeObject(cachedValue);
        byte b[] = bos.toByteArray();

        //Store in DB, file wherever here using b[]. I am not writing code to store it as it may vary in your requirement.

    } catch (IOException e) {
        e.printStackTrace();
    }

Read from byte array:

从字节数组中读取:

ByteArrayInputStream bis = new ByteArrayInputStream(<<read byte[] where you stored earlier>>);
    ObjectInput in = null;

    try {
        in = new ObjectInputStream(bis);
        <Your Class >cachedRes = ( Your Class) in.readObject();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

回答by Markus Lausberg

Why you have 2 CloassLoaders, which loads the same class? This could be a programatic issue. It sounds like you are caching ClassLoader somewhere and re-use them in a wrong way. If this is not the case try a MultiClassLoader.

为什么你有 2 个 CloassLoaders,它们加载同一个类?这可能是一个程序问题。听起来您正在某处缓存 ClassLoader 并以错误的方式重新使用它们。如果不是这种情况,请尝试使用 MultiClassLoader。

Create a MultiClassLoader which includes multiple other classloader. These MultiClassLoader you can use to load all Classes you wish. But you have to create these MultiClassLoader at the very beginning and not when the classes are loaded.

创建一个包含多个其他类加载器的 MultiClassLoader。您可以使用这些 MultiClassLoader 加载您希望的所有类。但是您必须在最开始而不是在加载类时创建这些 MultiClassLoader。

public class MultiClassLoader extends ClassLoader

You would have a collection of classloaders and in the findClass(...) you iterate over all these registered loaders.

您将拥有一组类加载器,并在 findClass(...) 中遍历所有这些已注册的加载器。

protected Class findClass(String aName) throws ClassNotFoundException {
   for (Iterator iter = multiLoaders.iterator(); iter.hasNext();) {
      ClassLoader tmpLoader = (ClassLoader)iter.next();
      try {
         return tmpLoader.loadClass(aName);
      } catch (ClassNotFoundException e) {
      }
   }
   throw new ClassNotFoundException(aName);
}

回答by Thorbj?rn Ravn Andersen

The easiest way is to use reflection. This allow you to dó anything you Can dó in "normal" code.

最简单的方法是使用反射。这允许您在“正常”代码中执行任何您可以执行的操作。