【问题标题】:Solution for the ClassCastException due to ClassLoader issue由于 ClassLoader 问题导致 ClassCastException 的解决方案
【发布时间】:2011-08-16 06:50:36
【问题描述】:

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

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

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

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

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

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

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

【问题讨论】:

  • 在您的实例中,如果您这样做:Object o = descriptor.getMojo(); System.out.println(o.getClass); 使用两个不同的类加载器会发生什么?
  • 这里最重要的问题是:您实际上想在这里实现什么?这是学术练习还是有支持这种情况的真实用例?
  • @Bringer128 它返回相同的类名 package.Mojo。我在想是否可以通过 getClass() 和 #cast(Object o) 方法执行上述转换?
  • @Sanjay T. Sharma 绝对不是。我正在修复现有代码。
  • @ravana 你见过this question吗?

标签: java classloader classcastexception


【解决方案1】:

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

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

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();
    }

从字节数组中读取:

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();
    }

【讨论】:

    【解决方案2】:

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

    • 一种解决方案是创建一个“通用”类加载器,它加载要由您的自定义类加载器使用的类。因此,在您的情况下,您将有一个新的类加载器来加载给定的类,而您的自定义类加载器将扩展这个类加载器。
    • 另一种解决方案是在两个类加载器之间传递“序列化”状态。将一个实例序列化为字节数组,并通过反序列化对象流在另一个类加载器中重建对象。

    【讨论】:

    • 我不想将它从一个类加载器转换到另一个。但这就是当我进行所说的铸造时发生的事情。我怎样才能在同一个类加载器中做到这一点? (Mojo) descriptor.getMojo() 不起作用!
    • 这是因为描述符中的“Mojo”实例是由不同的类加载器加载的,而不是您尝试在其中检索它的类加载器。我需要更多上下文。这是一个什么样的应用程序? Web应用程序?这里涉及多个线程吗?
    • 这是一个运行在单线程上的 java 应用。使用 classworlds 1.1 进行类加载。
    【解决方案3】:

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

    你会想要这样的东西:

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

    我遗漏了很多内容——尤其是异常处理——但这应该会引导您找到所需的 Javadoc。挺好看的,仔细看吧。

    如果您还拥有两个 Mojo 职业,则演员阵容将中断,您将不得不进行更多反思才能对邪恶双胞胎 Mojo 做任何您需要做的事情。

    【讨论】:

    • 确实是一个maven插件。我会试试这个,让你知道它是怎么回事。你的意思是什么javadoc?
    • 相关的类和方法javadoc。
    【解决方案4】:

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

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

    public class MultiClassLoader extends ClassLoader
    

    您将拥有一个类加载器集合,并在 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);
    }
    

    【讨论】:

    • 不幸的是我没有能力改变类加载器。我可以访问两者,但我无法创建像 MultipleClassloader 之类的东西!
    • getClass() 方法是否返回一个包含该类及其类加载器的类?如果是这样,有没有办法使用这样的机制来做到这一点?
    • 我一直在研究这个解决方案。我可以用另一种方式使用它。我想知道更多细节。说,我已经通过您的 findClass 方法获得了 Class 对象。那我该怎么办?正如我之前所说,我需要将我的对象转换为这个返回的类。 Mojo = class.cast(object) 给我编译器错误,因为它认为 class#cast 返回一个 Object 类型的对象。
    • 您可以像平常一样使用单个类加载器来实现您的代码。 multiclassloader 只处理 multilpe 类加载器,仅此而已。
    【解决方案5】:

    最简单的方法是使用反射。这允许你在“正常”代码中做任何你能做的事情。

    【讨论】:

    • 我明白了。给定的代码使用反射。但是我对反射的了解非常有限。您能否为答案添加一个简单的解决方案?这会很有帮助!
    • 使用反射时没有简单的解决方案。这很乏味:(
    【解决方案6】:

    我通过使用 Thread 的父上下文加载器加载完全限定的类解决了这个问题。

    作为一个例子,在这里使用 AbstractMojo。

           Thread
          .currentThread
          .getContextClassLoader
          .getParent
          .loadClass("org.apache.maven.plugin.AbstractMojo")

    【讨论】:

      猜你喜欢
      • 2012-02-07
      • 2016-04-18
      • 2014-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多