【问题标题】:ServiceLoader with UrlClassLoader not finding service带有 UrlClassLoader 的 ServiceLoader 找不到服务
【发布时间】:2020-12-21 09:37:57
【问题描述】:

我正在尝试使用带有 UrlClassLoader 的 java ServiceLoader 从某个 jar 文件加载插件,但我似乎无法让它找到我的插件类。构建两个模块都可以,但是每当我运行下面的代码时,我都会得到一个java.util.NoSuchElementException,即使文件路径是正确的并且我看不到我搞砸的地方

我必须在我的 IntelliJ 项目中添加模块,Test(加载插件并提供 ServiceProvider 的应用程序)和 PluginTest(要加载的插件)。

这是Test模块的结构截图:

下面是PluginTest模块的结构截图:

这里是 ServiceProvider PluginProvider:

package net.lbflabs;

public abstract class PluginProvider {

    public abstract String getSecretMessage(int message);
}

这是扩展这个类的插件:

package net.lbflabs.plugins;

import net.lbflabs.PluginProvider;

public class PluginClass extends PluginProvider {
    @Override
    public String getSecretMessage(int message) {
        return "Here is my secret.";
    }
}

这里是加载插件的主类:

package net.lbflabs;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ServiceLoader;

public class Main {

    private static String path = "C:/Users/jacke/IdeaProjects/Tests/out/artifacts/PluginTest_jar/PluginTest.jar";

    public static void main(String[] args) throws MalformedURLException {
        System.out.println("Initializing... \nPreparing to load JAR from Path " + path + "...");
        File file = new File(path);
        System.out.println(file.exists()); //Prints true, so file does exist
        URLClassLoader c = new URLClassLoader((new URL[]{file.getAbsoluteFile().toURI().toURL()}));
        ServiceLoader<PluginProvider> loader = ServiceLoader.load(PluginProvider.class, c);
        PluginProvider p = loader.iterator().next(); // Throws the java.util.NoSuchElementException
        System.out.println("Secret message in plugin: " + p.getSecretMessage(1));
    }
}

这是运行测试模块时的输出:

"C:\Program Files\Java\jdk-14.0.1\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3\lib\idea_rt.jar=50330:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\jacke\IdeaProjects\Tests\out\production\Tests net.lbflabs.Main
Initializing... 
Preparing to load JAR from Path C:/Users/jacke/IdeaProjects/Tests/out/artifacts/PluginTest_jar/PluginTest.jar...
true
Exception in thread "main" java.util.NoSuchElementException
    at java.base/java.util.ServiceLoader$2.next(ServiceLoader.java:1310)
    at java.base/java.util.ServiceLoader$2.next(ServiceLoader.java:1298)
    at java.base/java.util.ServiceLoader$3.next(ServiceLoader.java:1396)
    at net.lbflabs.Main.main(Main.java:19)

Process finished with exit code 1

我认为 PluginTest 模块中的服务文件命名正确 (net.lbflabs.PluginProvider) 并包含有效的类名 (net.lbflabs.plugins.PluginClass)。如果不正确,IntelliJ 可能会发现问题,因为当我引入错字时会收到警告。

如果有人需要,我可以将整个项目提供为 .rar(请告诉我您希望我如何分享它,例如我创建一个 onedrive 链接)

PS:我已经问过这个问题here,但是因为我正在做一个更大的项目并且犹豫要分享其中的所有代码(并且因为我得到的唯一答案没有用,可能是因为不够来自我的信息),我想用一个引发相同异常的最小可行示例重新发布该问题,我希望这很好。

【问题讨论】:

  • 类加载器不包含父类,只包含实现类,所以找不到父类。所以我怀疑由于不完整的类层次结构它无法加载该类。因此,您应该使用带有父类加载器并将其设置为当前类加载器的构造函数。这样就可以确定适当的类层次结构。
  • 那它不会抛出不同的异常吗?我的类加载器在类似的场合出现了 NoClassDefFoundError 之类的异常。你可能仍然是对的。我将如何解决这个问题?您能否将您的修复发布为答案,以便我可以接受它?
  • 不会是第一个异常被另一个隐藏并查看LazyClassPathLookupIterator(内部类)它实际上捕获ClassNotFoundException并简单地返回null。导致你得到的异常。

标签: java intellij-idea serviceloader


【解决方案1】:

我注意到的一件事是您构建了一个仅包含 1 个类的类加载器。有了这个,它将无法创建适当的类层次结构并且很可能会失败。而是将当前的 Classloader 作为父级传递给您的 URLClassLoader

ClassLoade parent = PluginProvider.class.getClassLoader();
URL[] urls = new URL[] { file.getAbsoluteFile().toURI().toURL()};
URLClassLoader c = new URLClassLoader(urls, parent);

现在应该可以构建适当的层次结构了。

另一件事是在您的项目结构中,您的META-INF 目录不在包含项目源代码的src 目录下。这意味着 Intellij 会将它们排除在罐子之外。所以你基本上是在添加一个 没有 正确文件的 jar。它只包含类而不包含META-INF/services/net.lbflabs.PluginProvider

【讨论】:

  • 谢谢。我确实将 PluginProvider 类加载器添加到了 urlclassloader,但它仍然无法正常工作
  • 但是您仍然得到相同的异常还是不同的异常?或者改用ClassLoader.getSystemClassLoader
  • 奇怪的事情,我无法重现。使用系统或 PluginProvider 的类加载器的确切示例确实得到了预期的结果。
  • 唯一的区别是我创建了一个 maven 项目而不是一个普通的 Java 项目。 jar 实际上是否包含带有服务目录和文件的 META-INF 目录? META-INF 需要在 src 中,否则它不会被导出到 jar 中。您的 jar 不包含文件,因此不包含插件。
  • 好吧,我怀疑真正的问题是您原始帖子中的META-INF 不在src 之下,这意味着省略了整个结构和服务加载程序文件。迁移到 maven 时,您可能会将 META-INF 放在 src/main/resources 中。
猜你喜欢
  • 2017-12-17
  • 1970-01-01
  • 2021-08-24
  • 1970-01-01
  • 2011-05-02
  • 2015-08-24
  • 2018-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多