因此,我一直在尝试在我的库中创建一个小的PluginLoader
,它允许您将JAR
文件加载到自定义的ModuleLayer
中,并使用该层加载使用ServiceLoader.load(ModuleLayer, Class<?>)
的服务。
然而,当调用ServiceProvider.load
时,它在内部使用Reflection.getCallerClass
来获取调用代码的类,这样它就可以从它的模块加载服务。
PluginLoader.java
package com.wexalian.common.plugin;
import com.wexalian.common.collection.wrapper.StreamWrapper;
import com.wexalian.nullability.annotations.Nonnull;
import java.nio.file.Path;
import java.util.ServiceLoader;
import java.util.stream.Stream;
@FunctionalInterface
public interface PluginLoader<T extends IAbstractPlugin> extends StreamWrapper.Iterable<T> {
@Nonnull
@Override
Stream<T> get();
static void init(@Nonnull ServiceLoaderLayerFunction serviceLoaderFunc) {
PluginLoaderImpl.init(serviceLoaderFunc);
}
static void loadPlugins(@Nonnull Path path) {
PluginLoaderImpl.loadPlugins(path);
}
@Nonnull
static <T extends IAbstractPlugin> PluginLoader<T> load(@Nonnull Class<T> pluginClass) {
return load(pluginClass, null);
}
@Nonnull
static <T extends IAbstractPlugin> PluginLoader<T> load(@Nonnull Class<T> pluginClass, ServiceLoaderFallbackFunction fallbackServiceProvider) {
return PluginLoaderImpl.load(pluginClass, fallbackServiceProvider);
}
@FunctionalInterface
interface ServiceLoaderLayerFunction {
@Nonnull
<T> ServiceLoader<T> load(@Nonnull ModuleLayer layer, @Nonnull Class<T> clazz);
@Nonnull
default <T> Stream<T> stream(@Nonnull ModuleLayer layer, @Nonnull Class<T> clazz) {
return load(layer, clazz).stream().map(ServiceLoader.Provider::get);
}
}
@FunctionalInterface
interface ServiceLoaderFallbackFunction {
@Nonnull
<T> ServiceLoader<T> load(@Nonnull Class<T> clazz);
@Nonnull
default <T> Stream<T> stream(@Nonnull Class<T> clazz) {
return load(clazz).stream().map(ServiceLoader.Provider::get);
}
}
}
PluginLoaderImpl.java
package com.wexalian.common.plugin;
import com.wexalian.nullability.annotations.Nonnull;
import java.io.IOException;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
final class PluginLoaderImpl {
private static final Set<ModuleLayer> pluginLayerSet = new HashSet<>();
private static PluginLoader.ServiceLoaderLayerFunction serviceLoaderLayer;
private static ModuleLayer coreLayer;
private static ClassLoader coreLoader;
private static boolean init = false;
private PluginLoaderImpl() {}
static void init(@Nonnull PluginLoader.ServiceLoaderLayerFunction serviceLoaderFunc) {
if (!init) {
serviceLoaderLayer = serviceLoaderFunc;
Class<?> coreClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass();
coreLayer = coreClass.getModule().getLayer();
coreLoader = coreClass.getClassLoader();
if (coreLayer == null) {
throw new IllegalStateException("PluginLoaderImpl can only be initialized from a named module!");
}
else init = true;
}
else throw new IllegalStateException("PluginLoaderImpl can only be initialized once!");
}
static void loadPlugins(@Nonnull Path path) {
if (init) {
if (Files.exists(path)) {
try (Stream<Path> paths = Files.list(path)) {
ModuleFinder moduleFinder = ModuleFinder.of(paths.toArray(Path[]::new));
List<String> moduleNames = moduleFinder.findAll().stream().map(ref -> ref.descriptor().name()).toList();
Configuration configuration = coreLayer.configuration().resolveAndBind(moduleFinder, ModuleFinder.of(), moduleNames);
ModuleLayer pluginLayer = coreLayer.defineModulesWithOneLoader(configuration, coreLoader);
pluginLayerSet.add(pluginLayer);
}
catch (IOException e) {
throw new IllegalStateException("Error loading plugins from path " + path, e);
}
}
}
else throw new IllegalStateException("PluginLoaderImpl has to be initialized before you can load plugins!");
}
static <T extends IAbstractPlugin> PluginLoader<T> load(Class<T> clazz, PluginLoader.ServiceLoaderFallbackFunction serviceLoader) {
if (init) {
if (!pluginLayerSet.isEmpty()) {
return () -> pluginLayerSet.stream().flatMap(layer -> serviceLoaderLayer.stream(layer, clazz)).filter(IAbstractPlugin::isEnabled);
}
else {
return () -> serviceLoaderLayer.stream(coreLayer, clazz).filter(IAbstractPlugin::isEnabled);
}
}
else if (serviceLoader != null) {
return () -> serviceLoader.stream(clazz);
}
else throw new IllegalStateException("PluginLoaderImpl has to be initialized before you can load services from plugins!");
}
}
现在我的问题是:我目前正在写一个带有一些服务的程序,并使用那个库来加载JAR文件并加载它们,但是它识别PluginLoader
作为调用者类,它“不声明uses”,因为库实际上并没有我想要的服务。
我已经找到了一个变通方法,即接受一个Function<ModuleLayer, Class<?>, ServiceProvider<?>
,它将所有调用重定向到适当的模块,但我不希望在使用PluginLoader
时都这样做。
除此之外我不知道其他解决办法,所以也许你们中的一个知道。
先谢了,韦克斯利安
1条答案
按热度按时间7lrncoxx1#
使用ModuleLayer系统时,您还必须在和各种插件模块定义中定义
uses
和provides
。您的模块:
在您的插件模块中:
参见ServiceLoader和ServiceLoader.Provider,这是一个模块中的服务加载器如何知道其他模块中的加载器。