2012-08-10 39 views
4

我有一个大的桌面Java应用程序,我想允许其他开发者开发插件。插件将放置在指定的目录中。他们在启动时不会在类路径中。我将在运行时加载和部署它们。动态的ClassLoader

的复杂性在于,一些插件将会对对方的依赖关系,以及核心应用。所以我不能加载每个插件/ jar在它自己的URLClassLoader中。因此我想将所有插件加载到1个URLClassLoader中。此外,由于各种原因,一些插件可能无法初始化。而且我只想在一天结束时知道成功加载插件的ClassLoader。原因很奇怪,并且涉及一些使用反射来实例化类的遗留问题。如果插件没有初始化在失败的插件jar中定义的类,这需要失败。

没有此要求,溶液将是:

  1. 收集罐的URL和建立基于他们类装入器
  2. 尝试从每个罐中(在清单中配置定义)初始化插件类

现在这里的ClassLoader会被传递给遗留系统以供它用于反射的东西。然而,这是我的理解,它仍然可以从它的插件未能初始化(因为罐子还是会在类加载器的URL [])插件罐类进行实例化。因此,这违反了我的要求。

唯一的解决方案,我想出了至今如下(简单地允许的findClass)访问()创建一个自定义的URLClassLoader:

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有关多个子类加载器:

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); 
    } 
} 

然后我装插件将算法FFT是类似

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实例传递到遗留系统,它只能找到类(通过反射),它的插件加载成功。

我已经做了一些基本的测试,它似乎工作,因为我想那么远。但我非常希望有人认为这是否是一个好主意?我从来没有玩过这么多与之前的ClassLoader,我想避免让自己陷得太深之前,为时已晚。

谢谢!

回答

4

我看到的问题是,如果你不提前知道哪个插件依赖于它,它很难做任何事合理,调试问题,隔离非功能性或坏行为的插件等

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

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

  • 为每个插件,你可以创建一个单独的类加载器将加载只是它需要的JAR文件。可能是最强大的解决方案。但是我看到一个缺点:作为许多其他依赖项的插件将在不同的类加载器中重复加载。这取决于具体情况(插件数量,JAR大小,...),如果这可能是一个问题,它甚至可能是一个优势。
  • 您可以像所有建议一样使用所有插件的大型ClassLoader,但您可以按照它们的依赖关系顺序要求插件类。那些不依赖于任何东西的人,那些依赖于那些第一等的人。如果一些插件类无法加载/初始化,你可以立即放弃依赖它的所有插件。
1

您是否正在寻找类似的OSGi的方法呢?

你可以像PetrPudlák所说的那样做,但是你应该考虑到你的解决方案之一可以创建循环依赖的事实......