我有一个大的桌面Java应用程序,我想允许其他开发者开发插件。插件将放置在指定的目录中。他们在启动时不会在类路径中。我将在运行时加载和部署它们。动态的ClassLoader
的复杂性在于,一些插件将会对对方的依赖关系,以及核心应用。所以我不能加载每个插件/ jar在它自己的URLClassLoader中。因此我想将所有插件加载到1个URLClassLoader中。此外,由于各种原因,一些插件可能无法初始化。而且我只想在一天结束时知道成功加载插件的ClassLoader。原因很奇怪,并且涉及一些使用反射来实例化类的遗留问题。如果插件没有初始化在失败的插件jar中定义的类,这需要失败。
没有此要求,溶液将是:
- 收集罐的URL和建立基于他们类装入器
- 尝试从每个罐中(在清单中配置定义)初始化插件类
现在这里的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,我想避免让自己陷得太深之前,为时已晚。
谢谢!