无法在Jar中的Jar接口插件类支持的Java应用程序中公开自定义类

无法在Jar中的Jar接口插件类支持的Java应用程序中公开自定义类,java,spring,classpath,Java,Spring,Classpath,我有一个使用WebSocket进行通信的服务器/客户端设置,它们都是Spring引导应用程序,用于通信的消息类保存在一个单独的项目“CommonClass”中 服务器和客户端项目的构建路径上都有CommonClass项目,这使它们能够访问共享消息类型 客户端将从服务器接收消息,并在响应之前对其进行操作。 迄今为止,客户机可以执行的功能数量有限,但其目的是向客户开放这些功能,以设计他们自己的实现。为此,必须实例化一个applier接口,我只需将客户机添加到一个新项目的类路径中,放入我的新实现,并在

我有一个使用WebSocket进行通信的服务器/客户端设置,它们都是Spring引导应用程序,用于通信的消息类保存在一个单独的项目“CommonClass”中

服务器和客户端项目的构建路径上都有CommonClass项目,这使它们能够访问共享消息类型

客户端将从服务器接收消息,并在响应之前对其进行操作。 迄今为止,客户机可以执行的功能数量有限,但其目的是向客户开放这些功能,以设计他们自己的实现。为此,必须实例化一个applier接口,我只需将客户机添加到一个新项目的类路径中,放入我的新实现,并在客户机中使用类路径加载器来加载这个新jar并调用新方法

如果我有一个普通的方法,但当我试图实现实际的接口时,它就不起作用了,该接口之前传递了CommonClass中详细介绍的消息

如果我解包客户机jar文件并将生成的CommonClasses jar文件添加到我的类路径中,我就可以看到这些类。但是,当我这样做并尝试将实例对象转换为我的接口类型时,我会得到一个java.lang.ClassCastException

我对堆栈溢出进行了研究,得出的结论是,尽管客户端接口和新项目中引用的接口具有相同的签名和项目,但它们被视为不同的对象。所有这些都是因为我必须从客户机jar中提取CommonClasses jar

我有什么选择? 我知道我可以创建一个新的接口,只使用普通的java类型并转换所有内容,但这会很昂贵,如果可能的话,我希望避免这种情况。 改变现有的项目构建过程是可能的,但如果可能的话应该避免

这就是我迄今为止所做的:

    URL[] classLoaderUrls = new URL[]{new URL("file:C:\\customApplier.jar")};
    // Create a new URLClassLoader
    URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls);
    // Load the target class
    Class<?> beanClass = urlClassLoader.loadClass("com.test.Applier");
    // Create a new instance from the loaded class
    Constructor<?> constructor = beanClass.getConstructor();
    ApplierIn ob = (ApplierIn)constructor.newInstance();
URL[]classLoaderUrls=newurl[]{newurl(“文件:C:\\customApplier.jar”)};
//创建一个新的URLClassLoader
URLClassLoader URLClassLoader=新的URLClassLoader(classLoaderUrls);
//加载目标类
类beanClass=urlClassLoader.loadClass(“com.test.Applier”);
//从加载的类创建新实例
Constructor=beanClass.getConstructor();
ApplierIn ob=(ApplierIn)构造函数.newInstance();

尝试强制转换到(ApplierIn)时引发异常。谢谢你的建议

多亏@Klaus Groenbaek能够识别问题,它确实是一个不正确的类装入器。所以我把它改为:

    private static void addCustomJar(URL fileUrl) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
    method.setAccessible(true);
    method.invoke(ApplierIn.class.getClassLoader(), new Object[]{fileUrl});
}

以便将新的jar文件加载到我的应用程序中。在那之后,事实证明,在一个罐子里打开的罐子不是一个问题

好的,给我一点时间,我会试着改写它。我突然想到的一件事是,如果Client.jar和Common.jar都包含
ApplierIn
接口,你可能会得到一个classcast异常。这听起来很奇怪。我以前使用过动态加载的JAR,除非不同的类加载器加载了多个版本的类,否则不应该出现这个问题。否则,无论何时在jar中实现
Comperator
,您都会遇到问题,因为在JRE jar中都可以找到
Comperator
String
。如果比较
ApplierIn.class.getClassLoader(),您应该能够调试用于加载实例的类加载器(在强制转换之前设置断点)
实例。getClass().getInterfaces()[0]。getClassLoader()
如果这不是同一个类加载器,那么在类路径中有多个
ApplierIn
接口。再次查看代码,看起来您没有设置父类加载器,您可能应该使用一个构造函数来设置父类加载器。我猜您希望使用
ApplierIn.class.getClassLoader()
作为父级,除非您创建了其他动态类加载器。您确定这是正确的解决方案吗。看起来您正在向一个已经实例化的类加载器添加一个额外的jar文件(可能是因为addURL受到保护)?如果这是真的,它有以下含义。1) 您的类加载器的大小将继续增长。2) 如果部署
MyApplier
并希望部署更新版本,您将遇到麻烦。我认为正确的解决方案是创建另一个具有正确父级的类加载器,以便在模块更新时丢弃它。您好,是的,这将是一种不寻常的行为,基本上我们有一个可以正常工作的产品,但我们有希望能够添加自定义实现的合作伙伴,在未来,它们的实现可能会进入我们的代码库,但现在的目的是给它们一个接口,并在我们的配置中允许它们指定jar的路径和定义包名。通常,模块不仅仅是一个Jar文件,因为它们具有第三方依赖关系,否则您必须非常明确容器提供了哪些库,以及在哪些版本中。