java - 从实现公共(public)接口(interface)的 jar 中实例化类,然后将实例分配给该接口(interface)会导致 ClassCastException

标签 java classpath classloader

这是我正在努力解决的一个类加载器问题。我了解问题的根本原因(不同的类加载器),但我不确定修复它的最佳方法。

我有一些通用接口(interface)的项目;我们称之为api。我还有另外两个名为 runnermodule 的项目,它们都使用 api 作为依赖项。

runner 的工作是动态加载一个 module 工件(从 jar 中;它是一个包含其依赖项的胖工件),然后执行它。 runner 期望 moduleapi 提供某些具体实现。为了确保来自 module.jar 的不同版本的类不会互相干扰,我创建了一个新的类加载器,其 URL 为 module.jar,并设置了父级classloader 到加载和处理 module.jar 的类的类加载器。这可以正常工作,没有任何问题。

当我使用runner作为Web应用程序(具体来说是一个Spring Boot应用程序)内的依赖项时,问题就出现了,并且很快发现我无法从模块加载一些类。 jar 因为它们与当前类路径中已存在的类(来自 web 应用程序中的其他依赖项)冲突。

由于module.jar实际上只需要api中的类,我认为我可以创建一个新的URLClassLoader(没有父级)仅包含来自 api.jar 的类,然后在加载模块时将其用作父类加载器。这就是我开始遇到麻烦的地方:

CommonInterface commonInterface = null;
Class<CommonInterface> commonInterfaceClass = null;

ClassLoader myClassLoader = URLClassLoader.newInstance(moduleJarURL, apiClassesClassLoader);

//...
//...

//clazz is a concrete implementation from module.jar
if(myClassLoader.loadClass(CommonInterface.class.getName()).isAssignableFrom(clazz)) {
    commonInterfaceClass = clazz; 
}

commonInterface = commonInterfaceClass.newInstance(); //ClassCastException

我知道我原来的问题是由于类加载器在尝试加载它之前首先检查该类是否已经加载,这意味着当它使用 module 中的名称解析时。 jar,它链接到该类的不兼容版本。

有什么好的方法可以解决这个问题吗?与其创建仅包含来自 api 的类的 URL 类加载器,不如创建我自己的实现,仅当请求的类是来自 api 的类时才将其委托(delegate)给父级,这是否有意义? ?

最佳答案

您已从两个不同的类加载器加载了CommonInterface。具有相同名称但不同类加载器的类对于 JVM 来说是不同的类。 (即使 .class 文件中的类 100% 相同 - 问题不是不兼容,而是它们来自不同的类加载器)

如果你做了

System.out.println(CommonInterface.class == myClassLoader.loadClass(CommonInterface.class.getName()));

您会发现这会打印 false

创建类加载器的方式:

ClassLoader myClassLoader = URLClassLoader.newInstance(moduleJarURL, apiClassesClassLoader);

.. 仅当 apiClassesClassLoader 也是包含此代码的类的父类加载器时才有效。

你可以尝试:

ClassLoader myClassLoader = URLClassLoader.newInstance(moduleJarURL, 
                                                       getClass().getClassLoader());

但是根据您的描述(它是一个包含自己的依赖项的“胖”jar)和复杂的 Web 类加载器(子加载器优先),这可能无法解决您的问题。

在这种情况下,唯一的解决方案是使模块 jar 变得“精简”,以确保仅使用一个类加载器加载每个类一次。

关于java - 从实现公共(public)接口(interface)的 jar 中实例化类,然后将实例分配给该接口(interface)会导致 ClassCastException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35075150/

相关文章:

java - 使用 ClassLoader 加载文件

java - 如何使用 Maven 将目录添加到 JAR 文件?

java - 启动托管服务器 WebLogic 12.2.1.4 时尝试同步群集 JNDI 树时出错

Java找不到符号编译错误

java - 以编程方式运行 TestNG,出现 "Cannot find class in classpath"异常

java - WebSphere类加载器和tomcat中的类加载器有什么不同吗?

java - 使用 WebDriverWait Boolean 的 NullPointerException

java - 如何构建/编译 C++、Java 和 Python 项目?

java - 通过批处理文件运行时会跳过测试,但在 eclipse ide 上执行

java - 类加载期间的内存分配