实现插件
本章节介绍如何实现一个实现一个自定义的 插件。
此处介绍的是一种简单的插件, 我们假设它的功能是安装后, 向事件调度器中注册一个事件处理器, 此处理器用来输出所有事件的信息日志的简单插件。
实现插件的基本步骤
实现接口 Plugin
。
如果需要的话, 实现一个配置类。
实现 PluginFactory
来提供工厂。
可选地支持 SPI
实现 Plugin 接口
实现一个插件的步骤其实与实现组件标识的步骤大差不差, 首先我们来使用类型 FooPlugin
实现接口 Plugin
。
public class FooPlugin implements Plugin {
}
喔, 看上去比实现一个 Component
还要简单呢!因为 Plugin
没有任何约束, 所以只需要实现它, 就可以了。
实现配置类
接下来, 我们提供一个对这个插件的配置类。因为事实上是没什么好配置的, 所以只需要提供一个空的配置类即可。
class FooPluginConfiguration
public class FooPluginConfiguration {
}
实现 PluginFactory
接下来, 为我们的插件类型实现它的工厂 PluginFactory
。
class FooPlugin : Plugin {
/** 伴生对象实现工厂 */
companion object Factory : PluginFactory<FooPlugin, FooPluginConfiguration> {
override val key: PluginFactory.Key = object : PluginFactory.Key {}
override fun create(context: PluginConfigureContext, configurer: ConfigurerFunction<FooPluginConfiguration>): FooPlugin {
configurer.invokeWith(FooPluginConfiguration())
// 此处预留空间, 实现功能
return FooPlugin()
}
}
}
public class FooPlugin implements Plugin {
/** 静态的唯一工厂实例。 */
public static final FooPluginFactory FACTORY = new FooPluginFactory();
/** FooPlugin 的工厂实现 */
public static class FooPluginFactory implements PluginFactory<FooPlugin, FooPluginConfiguration> {
private FooPluginFactory() {
}
/** Key 的单例唯一实现 */
private static final Key KEY_INSTANCE = new Key() {
};
@NotNull
@Override
public Key getKey() {
return KEY_INSTANCE;
}
@NotNull
@Override
public FooPlugin create(@NotNull PluginConfigureContext context, @NotNull ConfigurerFunction<? super FooPluginConfiguration> configurer) {
configurer.invoke(new FooPluginConfiguration());
// 此处预留空间, 实现功能
return new FooPlugin();
}
}
}
- key
此工厂用于安装注册时的唯一标记, 应当是一个唯一单例。 当 Application
对同一个类型的插件进行多次安装时, 就是使用这个 key
作为唯一标识来合并多次调用的。
- create(...)
提供一个插件配置上下文, 以及一个针对配置类的配置函数, 构建一个 FooPlugin
实例。
值得注意的是, 在示例代码中, 尽管配置类的实现是空的, 但是依旧构建了配置类并调用了配置函数。
作为插件的工厂实现, 应当尽可能确保配置函数至少被执行一次, 尽管配置类的实现可能是空的。
PluginConfigureContext
刚刚提到, create
的参数中有一个 PluginConfigureContext
类型的“插件配置上下文”参数, 它是由 Application
提供的, 里面包含了一些当前阶段可以提供的内容。
- applicationConfiguration
当前 Appliation
的配置信息。例如 CoroutineContext
信息。
- applicationEventRegistrar
一个 Application
的 “启动阶段事件” 注册器。此注册器中可以注册一些针对 Application
不同阶段的事件监听器。
在 Application
的启动时, 当到达了对应的阶段便会触发相应的事件。算是用于对 Application
自身的状态、流程的一种监听, 也可以用来在某个特定的阶段进行某些特定的处理。
- components
目前(上一步)构建得到的所有安装后的 Component
, 可以用来进行校验或获取一些配置等。
- eventDispatcher
Application
提供的事件调度器。 此时可以通过它来注册事件处理器, 或者保存下来在后续推送事件。
实现组件校验
我们在一开始提到过, 假设我们的 FooPlugin
是属于 FooComponent
的。 那么在构建 FooPlugin
的时候就应当对组件进行校验, 只有所需的组件被安装后, 才能继续。
修改我们的工厂实现, 增加校验逻辑:
class FooPlugin : Plugin {
companion object Factory : PluginFactory<FooPlugin, FooPluginConfiguration> {
override val key: PluginFactory.Key = object : PluginFactory.Key {}
override fun create(context: PluginConfigureContext, configurer: ConfigurerFunction<FooPluginConfiguration>): FooPlugin {
configurer.invokeWith(FooPluginConfiguration())
// 校验组件
if (context.components.find<FooComponent>() == null) {
// 如果没有对应ID的组件, 抛出 NoSuchComponentException 异常。
throw NoSuchComponentException(FooComponent.ID_VALUE)
}
// 此处预留空间, 实现功能
return FooPlugin()
}
}
}
public class FooPlugin implements Plugin {
public static final FooPluginFactory FACTORY = new FooPluginFactory();
public static class FooPluginFactory implements PluginFactory<FooPlugin, FooPluginConfiguration> {
// 其他内容省略..
@NotNull
@Override
public FooPlugin create(@NotNull PluginConfigureContext context, @NotNull ConfigurerFunction<? super FooPluginConfiguration> configurer) {
configurer.invoke(new FooPluginConfiguration());
// 校验组件
if (context.getComponents().findById(FooComponent.ID_VALUE) == null) {
// 如果没有对应ID的组件, 抛出 NoSuchComponentException 异常。
throw new NoSuchComponentException(FooComponent.ID_VALUE);
}
// 此处预留空间, 实现功能
return new FooPlugin();
}
}
}
实现功能:注册额外的事件处理器
接下来实现我们预期的功能:注册一个事件处理器, 这个处理器会处理所有的事件, 将这个事件输出到日志中。
class FooPlugin : Plugin {
companion object Factory : PluginFactory<FooPlugin, FooPluginConfiguration> {
override val key: PluginFactory.Key = object : PluginFactory.Key {}
override fun create(context: PluginConfigureContext, configurer: ConfigurerFunction<FooPluginConfiguration>): FooPlugin {
configurer.invokeWith(FooPluginConfiguration())
// 校验组件 省略...
// 注册事件处理器
val handle = context.eventDispatcher.register { eventContext ->
logger.info("Event: {}", eventContext.event)
// 返回一个普通的空结果
EventResult.empty()
}
return FooPlugin()
}
}
}
public class FooPlugin implements Plugin {
public static final FooPluginFactory FACTORY = new FooPluginFactory();
public static class FooPluginFactory implements PluginFactory<FooPlugin, FooPluginConfiguration> {
// 其他内容省略..
@NotNull
@Override
public FooPlugin create(@NotNull PluginConfigureContext context, @NotNull ConfigurerFunction<? super FooPluginConfiguration> configurer) {
configurer.invoke(new FooPluginConfiguration());
// 校验组件 省略...
// 注册事件处理器
var handle = context.getEventDispatcher().register(JAsyncEventListener.toListener(eventContext -> {
LOGGER.info("Event: {}", eventContext.getEvent());
// 返回一个普通的异步空结果
return CompletableFuture.completedFuture(EventResult.empty());
}));
return new FooPlugin();
}
}
}
public class FooPlugin implements Plugin {
public static final FooPluginFactory FACTORY = new FooPluginFactory();
public static class FooPluginFactory implements PluginFactory<FooPlugin, FooPluginConfiguration> {
// 其他内容省略..
@NotNull
@Override
public FooPlugin create(@NotNull PluginConfigureContext context, @NotNull ConfigurerFunction<? super FooPluginConfiguration> configurer) {
configurer.invoke(new FooPluginConfiguration());
// 校验组件 省略...
// 注册事件处理器
var handle = context.getEventDispatcher().register(JBlockingEventListener.toListener(eventContext -> {
LOGGER.info("Event: {}", eventContext.getEvent());
// 返回一个普通的空结果
return EventResult.empty();
}));
return new FooPlugin();
}
}
}
完善功能:增加 FooPlugin 的信息
你可能发现了, 在上述示例中注册完事件处理器后返回了一个 handle
, 这个 handle
是注册事件处理器后的“回执”类型 EventListenerRegistrationHandle
, 它可以在后续希望取消此事件处理器的时候使用 dispose
来取消。
val handle = dispatcher.register(...)
handle.dispose() // 取消注册
var handle = dispatcher.register(...);
handle.dispose(); // 取消注册
如果你希望允许用户在后续从 FooPlugin
中取消你所注册的功能, 那么我们便可以将这个 handle
记录在我们的 FooPlugin
中。
class FooPlugin(val handle: EventListenerRegistrationHandle) : Plugin {
companion object Factory : PluginFactory<FooPlugin, FooPluginConfiguration> {
override val key: PluginFactory.Key = object : PluginFactory.Key {}
override fun create(context: PluginConfigureContext, configurer: ConfigurerFunction<FooPluginConfiguration>): FooPlugin {
configurer.invokeWith(FooPluginConfiguration())
// 校验组件, 省略
// 注册事件处理器, 省略
val handle = context.register(...)
// 记录到 FooPlugin 中
return FooPlugin(handle)
}
}
}
public class FooPlugin implements Plugin {
public static final FooPluginFactory FACTORY = new FooPluginFactory();
/** 记录 handle */
private final EventListenerRegistrationHandle handle;
public FooPlugin(EventListenerRegistrationHandle handle) {
this.handle = handle;
}
/** 获取 handle */
public EventListenerRegistrationHandle getHandle() {
return handle;
}
public static class FooPluginFactory implements PluginFactory<FooPlugin, FooPluginConfiguration> {
// 其他内容省略..
@NotNull
@Override
public FooPlugin create(@NotNull PluginConfigureContext context, @NotNull ConfigurerFunction<? super FooPluginConfiguration> configurer) {
configurer.invoke(new FooPluginConfiguration());
// 校验组件, 省略
// 注册事件处理器, 省略
var handle = context.getEventDispatcher().register(...);
// 记录到 FooPlugin 中
return new FooPlugin(handle);
}
}
}
如此一来, FooPlugin
这个类型本身也具有了一定的功能性。
支持 SPI
实现 PluginFactoryProvider
接下来, 为我们的 FooPlugin.Factory
实现一个可以通过 SPI 加载的供应者。
PluginFactory
的作用是构建 Plugin
, 而 PluginFactoryProvider
的作用则是加载 PluginFactory
。
class FooPluginFactoryProvider : PluginFactoryProvider<FooPluginConfiguration> {
override fun configurersLoader(): Sequence<PluginFactoryConfigurerProvider<FooPluginConfiguration>>? {
return null
}
override fun provide(): PluginFactory<*, FooPluginConfiguration> {
return FooPlugin.Factory
}
}
public class FooPluginFactoryProvider implements PluginFactoryProvider<FooPluginConfiguration> {
@Nullable
@Override
public Sequence<PluginFactoryConfigurerProvider<FooPluginConfiguration>> configurersLoader() {
return null;
}
@NotNull
@Override
public PluginFactory<?, FooPluginConfiguration> provide() {
return FooPlugin.FACTORY;
}
}
添加 services 文件
接下来, 在你的项目资源目录的 resources/META-INF/services 中创建一个文件: love.forte.simbot.plugin.PluginFactoryProvider
, 并在其中填入你的实现类的全限定名称, 例如:
com.example.foo.FooPluginFactoryProvider
修改 module-info.java
同样的, 如果你提供了模块化信息文件 module-info.java
, 你还需要在其中补充上相关信息:
import love.forte.simbot.plugin.PluginFactoryProvider;
module com.example.foo {
// requires、exports 等内容省略
provides PluginFactoryProvider with com.example.foo.FooPluginFactoryProvider;
}
结束
以上就是包括必要操作、建议行为等内容在内的完整的实现一个 插件 的步骤了。
现在你可以将你的插件安装在任意的 Application
中, 或者在 Spring Boot 等支持 SPI 的地方自动安装啦~
val app = launchSimpleApplication {
install(FooPlugin)
}
// Kotlin 可以直接通过扩展函数根据类型寻找目标
val fooPlugin = app.plugins.find<FooPlugin>()
println(fooPlugin)
Applications.launchApplicationAsync(Simple.INSTANCE, configurer -> {
// 注册
configurer.install(FooPlugin.FACTORY);
}).asFuture().thenAccept(app -> {
// 寻找注册的组件中的 FooPlugin
var fooComponent = app.getPlugins().stream()
.filter(plugin -> plugin instanceof FooPlugin)
.findFirst()
.orElse(null);
System.out.println(fooComponent);
});
final var app = Applications.launchApplicationBlocking(Simple.INSTANCE, configurer -> {
// 注册
configurer.install(FooPlugin.FACTORY);
});
// 寻找注册的组件中的 FooPlugin
var fooPlugin = app.getPlugins().stream()
.filter(plugin -> plugin instanceof FooPlugin)
.findFirst()
.orElse(null);
System.out.println(fooPlugin);
Last modified: 18 January 2025