当前位置: 首页 > 知识库问答 >
问题:

码头的ServiceLoader问题

公冶森
2023-03-14

我正在开发一个web应用程序,它应该能够在运行时加载插件。我知道OSGi将是一个优雅的解决方案,但由于使用了GWT,我看不到向OSGi的过渡。

为了实现插件,我有3个JAR:应用程序、插件API:

public interface NotificationPlugin {

   public String getName();
}

和插件。罐子

public class Plugin implements NotificationPlugin {
   private final String PLUGIN_NAME = "Plugin";    
   @Override 
   public String getName() {
       return PLUGIN_NAME;
   }
}

plugin.jar和Web应用程序依赖于pluginapi.jar.在应用程序中,我使用单独的URLClassloader加载每个jar文件,因此它们可以单独卸载。

    service.setUcl(new URLClassLoader(url));

    ServiceLoader<NotificationPlugin> sl = ServiceLoader.load(NotificationPlugin.class, service.getUcl());
    Iterator<NotificationPlugin> apit = sl.iterator();
    service.setPlugin(apit.next());

    while (apit.hasNext()) {
            System.out.println(apit.next().getClass().getName());
        }  

现在,当且仅当我在应用服务器之外运行代码时,上面的代码片段才能发挥作用。我们使用Jetty在netbeans中调试GWT,应用程序部署在Tomcat上(两者表现出相同的行为)。只要我在应用服务器中运行代码,代码就会在ServiceLoader中失败。java:

public S next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            fail(service,
                 "Provider " + cn + " not found");
        }
 --->   if (!service.isAssignableFrom(c)) {   <-----
            fail(service,
                 "Provider " + cn  + " not a subtype");
        }
        try {
            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service,
                 "Provider " + cn + " could not be instantiated: " + x,
                 x);
        }
        throw new Error();          // This cannot happen
    }

调试显示,插件和接口都已加载,在我看来是正确的,否则Class.for将失败。同样,如果没有应用程序服务器,这不会发生。服务信息位于META-INF/services/xx.xx.pluginapi.NotificationPlugin

我的直觉告诉我,这个错误与应用程序服务器使用ClassLoaders的方式有关,但我和谷歌没有运气找到任何参考。有人知道如何克服这个问题吗?感谢帮助!

共有2个答案

欧阳安阳
2023-03-14

我在jette中开始使用war文件时遇到了问题。这是不同类加载器的问题。

当我使用通过getClass()获取的类加载器时,它可以工作。getClassLoader()

loader = ServiceLoader.load(Extractor.class, new URLClassLoader(new URL[]{toUrl(pluginPath)}, getClass().getClassLoader()));
章彬郁
2023-03-14

看见https://www.ibm.com/developerworks/java/library/j-dyn0429/尤其是本指南:

当使用多个类加载器时,也可能出现其他类型的混淆。图2显示了一个类身份危机的示例,当接口和相关实现分别由两个单独的类加载器加载时,会产生这种危机。即使接口和类的名称和二进制实现是相同的,但不能将一个加载程序中的类实例识别为从另一个加载程序中实现接口。

然后,回想一下@tom写道:

在应用程序中,我用一个单独的URLClassloader加载每个jar文件,因此可以单独卸载它们。

解决方案——尽管可能不容易实现——是为接口和实现接口的类使用相同的类加载器。

 类似资料:
  • 问题内容: 我尝试使用Java ServiceLoader查找实现特定接口的所有类,如下所示: 不幸的是,当我在调试模式下运行Eclipse时,ServiceLoader找不到任何类。我觉得我错过了一个琐碎的事情… 问题答案: 无法做到。 为了将类公开为可以被发现的服务,您需要将其名称放入提供程序配置文件中,如使用Java平台创建可扩展应用程序中所述 。 没有内置的方法可以找到实现特定接口的所有类

  • 问题内容: 如何在运行时在实现已定义接口的类路径中发现类? ServiceLoader非常适合(我认为,我还没有使用过),但是我需要在Java 1.5中做到。 问题答案: Java 1.5没有为此内置任何功能。我自己实现了它;不太复杂。但是,当我们升级到Java 6时,必须将对实现的调用替换为对的调用。我本可以在应用程序和加载器之间定义一个小桥梁,但是我只在少数几个地方使用它,而包装器本身将是Se

  • 本文向大家介绍详谈ServiceLoader实现原理,包括了详谈ServiceLoader实现原理的使用技巧和注意事项,需要的朋友参考一下 在java中根据一个子类获取其父类或接口信息非常方便,但是根据一个接口获取该接口的所有实现类却没那么容易。 有一种比较笨的办法就是扫描classpath所有的class与jar包中的class,然后用ClassLoader加载进来,然后再判断是否是给定接口的子

  • 我有一个pom,我用以下方式配置了jetty插件: 但是当码头停下来时,我得到了例外: 我尝试将带有jetty服务器库的依赖项部分添加到插件中,但没有成功。

  • 我正在尝试制作一个Java应用程序,它可以加载实现抽象类的插件,并且在ServiceLoader生成的实例中出现了AbstractMethodError。代码有点重,所以我在下面进行了简化。 首先,我有一个抽象类: 它实现了以下接口: 我有一个服务提供商,它扩展了抽象类,并在meta-inf/services文件夹中声明: 然后在主应用程序(实际上是另一个应用程序中的插件)上,我想找到扩展Abst

  • 我正在使用java。util。ServiceLoader创建轻量级插件框架。 我目前正在努力解决如何拥有多个具有相同FQN的实现类。我想在类路径上拥有同一个插件的两个副本,并且可以访问META-INF/services目录中给出的两个实现类。 以下是一些简单的eclipse项目,它们说明了我的意思:https://docs.google.com/open?id=0B4MxFm-ACB3IUmswN