В Java есть класс для этого: ServiceLoader .
Если мы предполагаем, что у вас есть интерфейс «поставщика услуг» с именем PluginProvider
, другие модули могут заявить о себе, чтобы предоставить эту услугу, добавив это в соответствующие дескрипторы module-info.java:
provides com.john.myapp.PluginProvider with com.library.MyProvider;
Тогда ваше приложение заявит, что оно использует эту службу в своей собственной информации модуля:
uses com.john.myapp.PluginProvider;
И код вашего приложения создаст ModuleFinder , который ищет в каталоге (или каталогах), где вы ожидаете, что эти подключаемые модули находятся, а затем передает этот ModuleFinder в Конфигурацию , которая может использоваться для создания ModuleLayer для ServiceLoader:
public class PluginLoader {
private final ServiceLoader<PluginProvider> loader;
public PluginLoader() {
Path pluginDir = Paths.get(System.getProperty("user.home"),
".local", "share", "MyApplication", "plugins");
ModuleLayer layer = PluginProvider.class.getModule().getLayer();
layer = layer.defineModulesWithOneLoader(
layer.configuration().resolveAndBind(
ModuleFinder.of(),
ModuleFinder.of(pluginDir),
Collections.emptySet()),
PluginProvider.class.getClassLoader());
loader = ServiceLoader.load(layer, PluginProvider.class);
}
public Stream<PluginProvider> getAll() {
return loader.stream();
}
public void reload() {
loader.reload();
}
}
Возможно, вы даже захотите посмотреть каталог плагинов для новых или удаленных файлов:
try (WatchService watch = pluginDir.getFileSystem().newWatchService()) {
pluginDir.register(watch,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.OVERFLOW);
WatchKey key;
while ((key = watch.take()).isValid()) {
loader.reload();
key.reset();
}
}