Java应用插件加载器开发故障:ServiceLoader返回空列表
Java插件系统加载故障排查
我正在开发Java应用的插件系统,需求是:指定一个存放插件jar包的目录,应用启动时自动加载其中的插件。目前已完成插件结构依赖并准备了测试插件,但加载系统出现异常。参考过一篇博客的类加载器实现,但无法正常工作。
期望的插件系统目录结构
C:. ├───commands │ ├───plugin │ │ └plugin.jar │ └───plugin2 │ └plugin2.jar └───src └───Java app ├───controller │ └───loaders ├───model └───view
loaders包中的实现类
PluginLoader.java
public class PluginLoader { private final Map<String, CommandFactory> commandFactoryMap = new HashMap<>(); private final File pluginsDir; private final AtomicBoolean loading = new AtomicBoolean(); public PluginLoader(final File pluginsDir) { this.pluginsDir = pluginsDir; } public void loadCommands() { if (!pluginsDir.exists() || !pluginsDir.isDirectory()) { System.err.println("Skipping Plugin Loading. Plugin dir not found: " + pluginsDir); return; } if (loading.compareAndSet(false, true)) { final File[] files = requireNonNull(pluginsDir.listFiles()); for (File pluginDir : files) { if (pluginDir.isDirectory()) { loadCommand(pluginDir); } } } } private void loadCommand(final File pluginDir) { System.out.println("Loading plugin: " + pluginDir); final URLClassLoader pluginClassLoader = createPluginClassLoader(pluginDir); final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(pluginClassLoader); // 此处循环无数据返回 for (OmenChatPlugin omenChatPlugin : ServiceLoader.load(OmenChatPlugin.class, pluginClassLoader)) { installPlugin(omenChatPlugin); } } finally { Thread.currentThread().setContextClassLoader(currentClassLoader); } } private void installPlugin(final OmenChatPlugin plugin) { System.out.println("Installing plugin: " + plugin.getClass().getName()); for (CommandFactory c : plugin.getCommandFactories()) { commandFactoryMap.put(c.name(), c); } } private URLClassLoader createPluginClassLoader(File dir) { final URL[] urls = Arrays.stream(Optional.of(dir.listFiles()).orElse(new File[]{})) .sorted() .map(File::toURI) .map(this::toUrl) .toArray(URL[]::new); System.out.println("cpcl: " + Arrays.toString(urls)); return new PluginClassLoader(urls, getClass().getClassLoader()); } private URL toUrl(final URI uri) { try { return uri.toURL(); } catch (MalformedURLException e) { throw new RuntimeException(e); } } public CommandFactory getCommandFactory(String name) { return commandFactoryMap.get(name); } }
PluginClassLoader.java
public class PluginClassLoader extends URLClassLoader { public static final List<String> SHARED_PACKAGES = Arrays.asList( "com.github.OMEN44", "com.github.omen" ); private final ClassLoader parentClassLoader; public PluginClassLoader(URL[] urls, ClassLoader parentClassLoader) { super(urls, null); this.parentClassLoader = parentClassLoader; } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // has the class loaded already? Class<?> loadedClass = findLoadedClass(name); if (loadedClass == null) { final boolean isSharedClass = SHARED_PACKAGES.stream().anyMatch(name::startsWith); if (isSharedClass) { loadedClass = parentClassLoader.loadClass(name); } else { loadedClass = super.loadClass(name, resolve); } } if (resolve) { // marked to resolve resolveClass(loadedClass); } return loadedClass; } }
问题现象
启动应用时,createPluginClassLoader方法打印的URL正常,但标注的for循环中,ServiceLoader.load(OmenChatPlugin.class, pluginClassLoader)返回空列表,无法加载到插件实例。寻求解决方案或更优的插件系统实现思路。
内容的提问来源于stack exchange,提问作者Huon Swales
相关产品推荐
相关产品推荐

