首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >动态ClassLoader

动态ClassLoader
EN

Stack Overflow用户
提问于 2012-08-10 02:38:39
回答 2查看 1.6K关注 0票数 3

我有一个大型桌面Java应用程序,我希望允许其他开发人员为其开发插件。插件将是放在指定dir中的jars。它们在启动时不会出现在类路径上。我将在运行时加载和部署它们。

复杂的是,一些插件将相互依赖,以及核心应用程序。因此,我无法在自己的URLClassLoader中加载每个插件/jar。因此,我想将所有插件加载到1 URLClassLoader中。此外,由于各种原因,一些插件可能无法初始化。最后,我只想要一个知道成功加载插件的ClassLoader。原因是非常奇怪的,并与一些遗留的东西有关,即使用反射来实例化类。如果插件没有为失败的插件jar中定义的类初始化,这就需要失败。

如果没有这一要求,解决办法将是:

  1. 收集jar URL并基于它们构建一个ClassLoader
  2. 尝试从每个jar初始化一个插件类(在清单中的config中定义)

现在,这里的ClassLoader将被传递给遗留系统,以便用于其反射内容。然而,我的理解是,它仍然能够从插件jar中实例化类,这些插件无法初始化(因为jar仍然在URL[] of ClassLoader中)。因此,这违反了我上面的要求。

到目前为止,我提出的唯一解决方案是创建一个自定义URLClassLoader,如下所示(只允许访问findClass()):

代码语言:javascript
复制
public class CustomURLClassLoader extends URLClassLoader {

    public CustomURLClassLoader(final URL[] urls, final ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        return super.findClass(name);
    }
}

然后,我做了另一个定制的ClassLoader,它本质上了解多个子ClassLoaders:

代码语言:javascript
复制
public class MultiURLClassLoader extends ClassLoader {

    private Set<CustomURLClassLoader> loaders = new HashSet<CustomURLClassLoader>();

    public MultiURLClassLoader(final ClassLoader parent) {
        super(parent);
    }

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        Iterator<CustomURLClassLoader> loadersIter = loaders.iterator();
        boolean first = true;
        while (first || loadersIter.hasNext()) {
            try {
                if (first) {
                    return super.findClass(name);
                } else {
                    return loadersIter.next().findClass(name);
                }
            } catch (ClassNotFoundException e) {
                first = false;
            }
        }
        throw new ClassNotFoundException(name);
    }

    public void addClassLoader(final CustomURLClassLoader classLoader) {
        loaders.add(classLoader);
    }

    public void removeClassLoader(final CustomURLClassLoader classLoader) {
        loaders.remove(classLoader);
    }
}

然后我的加载插件算法将类似于

代码语言:javascript
复制
MultiURLClassLoader multiURLClassLoader = new MultiURLClassLoader(ClassLoader.getSystemClassLoader());
for (File pluginJar : new File("plugindir").listFiles()) {
    CustomURLClassLoader classLoader = null;
    try {
        URL pluginURL = pluginJar.toURI().toURL();
        final URL[] pluginJarUrl = new URL[] { pluginURL };
        classLoader = new CustomURLClassLoader(pluginJarUrl, multiURLClassLoader);
        multiURLClassLoader.addClassLoader(classLoader);
        Class<?> clazz = Class.forName("some.PluginClass", false, multiURLClassLoader);
        Constructor<?> ctor = clazz.getConstructor();
        SomePluginInterface plugin = (SomePluginInterface)ctor1.newInstance();
        plugin.initialise();            
    } catch (SomePluginInitialiseException e) {
        multiURLClassLoader.removeClassLoader(classLoader);
    }
}

然后,我可以将multiURLClassLoader实例传递到遗留系统上,它只能找到成功加载插件的类(通过反射)。

我已经做了一些基本的测试,到目前为止,它似乎像我想的那样起作用了。但我非常希望有人认为这是否是个好主意?我以前从来没有和ClassLoaders打过这么多的球,我想避免在为时已晚之前让自己陷得太深。

谢谢!

EN

回答 2

Stack Overflow用户

发布于 2012-08-10 19:42:46

我看到的问题是,如果事先不知道哪个插件取决于哪个插件,那么很难做任何合理的事情,调试问题,隔离非功能性或行为不良的插件等等。

因此,我建议另一个选项:在每个插件的清单中添加另一个字段,这将说明它依赖于哪些其他插件。也许只是一个其他插件的列表,它需要运行。(核心应用程序类总是可用的。)我相信这将使设计更加健壮,并简化许多事情。

然后,您可以从不同的设计中选择,例如:

  • 对于每个插件,您可以创建一个单独的ClassLoader,只加载它所需的JAR。可能是最有力的解决方案。但我看到了一个缺点:作为许多其他插件的依赖项的插件将在不同的类加载器中反复加载。这取决于环境(插件数量,罐子大小,.)如果这可能是一个问题或不,它甚至可能是一个优势。
  • 您可以为所有插件提供一个大的ClassLoader,正如您所建议的那样,但是您可以按照插件类的依赖关系的顺序来请求它。如果某个插件类无法加载/初始化,您可以立即丢弃所有依赖它的插件。
票数 4
EN

Stack Overflow用户

发布于 2013-07-08 13:07:24

你在寻找类似于OSGi方法的东西吗?

您可以像Petr Pudlák所说的那样做一些事情,但是您应该考虑到这样一个事实:您拥有的解决方案之一可以创建循环依赖关系.

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11894721

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档