Simple Robot v4.6.0 Help

实现组件标识

组件标识 是实现一个组件的基础。

实现组件标识的基本步骤

  1. 实现接口 Component

  2. 如果需要的话, 实现一个配置类。

  3. 实现 ComponentFactory 来提供工厂。

  4. 可选地支持 SPI

实现 Component 接口

假设 你想要实现一个叫做 FooComponent 的组件标识, 且它的 id 为 com.example.foo

接下来, 我们先来实现它, 定义此类型并实现 Component 接口:

class FooComponent : Component { override val id: String = "com.example.foo" override val serializersModule: SerializersModule = EmptySerializersModule() }
public class FooComponent implements Component { @NotNull @Override public String getId() { return "com.example.foo"; } @NotNull @Override public SerializersModule getSerializersModule() { return SerializersModuleBuildersKt.EmptySerializersModule(); } }
id

这个组件的唯一标识。建议使用一个不会被重复的命名, 例如类似于 Java 包的命名方式, 域名倒置 + . + 简短的组件名称 等。

serializersModule

以组件为单位向 Application 提供一切可能需要用到的序列化信息, 例如 SerializableBotConfigurationMessage.Element 实现。

实现配置类

由于我们没有什么需要配置的东西, 因此仅提供一个空的配置类即可。

class FooComponentConfiguration
public class FooComponentConfiguration { }

实现 ComponentFactory

接下来, 实现 ComponentFactory, 使其用于构建我们的 FooComponent

class FooComponent : Component { override val id: String = "com.example.foo" override val serializersModule: SerializersModule = EmptySerializersModule() /** 伴生对象实现工厂 */ companion object Factory : ComponentFactory<FooComponent, FooComponentConfiguration> { override val key: ComponentFactory.Key = object : ComponentFactory.Key {} override fun create(context: ComponentConfigureContext, configurer: ConfigurerFunction<FooComponentConfiguration>): FooComponent { FooComponentConfiguration().invokeBy(configurer) return FooComponent() } } }
public class FooComponent implements Component { /** 静态的唯一工厂实例。 */ public static final FooComponentFactory FACTORY = new FooComponentFactory(); @NotNull @Override public String getId() { return "com.example.foo"; } @NotNull @Override public SerializersModule getSerializersModule() { return SerializersModuleBuildersKt.EmptySerializersModule(); } /** FooComponent 的工厂实现 */ public static class FooComponentFactory implements ComponentFactory<FooComponent, FooComponentConfiguration> { private FooComponentFactory(){ } /** Key 的单例唯一实现 */ private static final Key KEY_INSTANCE = new Key() { }; @NotNull @Override public Key getKey() { return KEY_INSTANCE; } @NotNull @Override public FooComponent create(@NotNull ComponentConfigureContext context, @NotNull ConfigurerFunction<? super FooComponentConfiguration> configurer) { configurer.invoke(new FooComponentConfiguration()); return new FooComponent(); } } }
key

此工厂用于安装注册时的唯一标记, 应当是一个唯一单例。 当 Application 对同一个类型的组件进行多次安装时, 就是使用这个 key 作为唯一标识来合并多次调用的。

create(...)

提供一个组件配置上下文, 以及一个针对配置类的配置函数, 构建一个 FooComponent 实例。

值得注意的是, 在示例代码中, 尽管配置类的实现是空的, 但是依旧构建了配置类并调用了配置函数。

作为组件的工厂实现, 应当尽可能确保配置函数至少被执行一次, 尽管配置类的实现可能是空的。

ComponentConfigureContext

刚刚提到, create 的参数中有一个 ComponentConfigureContext 类型的“组件配置上下文”参数, 它是由 Application 提供的, 里面包含了一些当前阶段可以提供的内容。

applicationConfiguration

当前 Appliation 的配置信息。例如 CoroutineContext 信息。

applicationEventRegistrar

一个 Application“启动阶段事件” 注册器。此注册器中可以注册一些针对 Application 不同阶段的事件监听器。

Application 的启动时, 当到达了对应的阶段便会触发相应的事件。算是用于对 Application 自身的状态、流程的一种监听, 也可以用来在某个特定的阶段进行某些特定的处理。

整理代码

其实至此, 一个最简单的组件标识和它的工厂实现已经结束了。但是对于一个组件标识的实现, 我们有一些非硬性的建议:

id 常量化

对于 Component.id, 我们建议直接作为常量且对外公开, 以应对一些不构建 Component 便可获取 id 值的场景。

serializersModule 静态化

对于 Component.serializersModule, 我们建议对其的获取静态化, 以应对一些不构建 Component 便可获取 serializersModule 信息的场景。

实现 Provider 来支持 SPI 自动加载

建议再实现一个 ComponentFactoryProvider 来支持部分场景下(例如 Spring Boot 环境) 通过 SPI 自动加载你的组件。 下文会详细介绍。

根据上述的建议(除了 SPI 实现), 我们整理一下现有的代码, 让它们符合这些建议。

class FooComponent : Component { override val id: String get() = ID_VALUE override val serializersModule: SerializersModule get() = Factory.SerializersModule /** 伴生对象实现的工厂实现 */ companion object Factory : ComponentFactory<FooComponent, FooComponentConfiguration> { /** id 常量化 */ const val ID_VALUE: String = "com.example.foo" /** serializersModule "静态化" */ @JvmField val SerializersModule: SerializersModule = EmptySerializersModule() override val key: ComponentFactory.Key = object : ComponentFactory.Key {} override fun create(context: ComponentConfigureContext, configurer: ConfigurerFunction<FooComponentConfiguration>): FooComponent { FooComponentConfiguration().invokeBy(configurer) return FooComponent() } } }
public class FooComponent implements Component { /** id 常量化 */ public static final String ID_VALUE = "com.example.foo"; /** serializersModule 静态化 */ public static final SerializersModule SerializersModule = SerializersModuleBuildersKt.EmptySerializersModule(); /** 静态的唯一工厂实例。 */ public static final FooComponentFactory FACTORY = new FooComponentFactory(); @NotNull @Override public String getId() { return ID_VALUE; } @NotNull @Override public SerializersModule getSerializersModule() { return SerializersModule; } /** FooComponent 的工厂实现 */ public static class FooComponentFactory implements ComponentFactory<FooComponent, FooComponentConfiguration> { private FooComponentFactory(){ } /** Key 的单例唯一实现 */ private static final Key KEY_INSTANCE = new Key() { }; @NotNull @Override public Key getKey() { return KEY_INSTANCE; } @NotNull @Override public FooComponent create(@NotNull ComponentConfigureContext context, @NotNull ConfigurerFunction<? super FooComponentConfiguration> configurer) { configurer.invoke(new FooComponentConfiguration()); return new FooComponent(); } } }

支持 SPI

实现 ComponentFactoryProvider

接下来, 为我们的 FooComponent.Factory 实现一个可以通过 SPI 加载的供应者。

ComponentFactory 的作用是构建 Component, 而 ComponentFactoryProvider 的作用则是加载 ComponentFactory

class FooComponentFactoryProvider : ComponentFactoryProvider<FooComponentConfiguration> { override fun loadConfigurers(): Sequence<ComponentFactoryConfigurerProvider<FooComponentConfiguration>>? { return null } override fun provide(): ComponentFactory<*, FooComponentConfiguration> { return FooComponent.Factory } }
public class FooComponentFactoryProvider implements ComponentFactoryProvider<FooComponentConfiguration> { @Nullable @Override public Sequence<ComponentFactoryConfigurerProvider<FooComponentConfiguration>> loadConfigurers() { return null; } @NotNull @Override public ComponentFactory<?, FooComponentConfiguration> provide() { return ooComponent.FACTORY; } }

添加 services 文件

接下来, 在你的项目资源目录的 resources/META-INF/services 中创建一个文件: love.forte.simbot.component.ComponentFactoryProvider, 并在其中填入你的实现类的全限定名称, 例如:

com.example.foo.FooComponentFactoryProvider

修改 module-info.java

同样的, 如果你提供了模块化信息文件 module-info.java, 你还需要在其中补充上相关信息:

import love.forte.simbot.component.ComponentFactoryProvider; module com.example.foo { // requires、exports 等内容省略 provides ComponentFactoryProvider with com.example.foo.FooComponentFactoryProvider; }

结束

以上就是包括必要操作、建议行为等内容在内的完整的实现一个 组件标识 的步骤了。

现在你可以将你的组件安装在任意的 Application 中, 或者在 Spring Boot 等支持 SPI 的地方自动安装啦~

val app = launchSimpleApplication { install(FooComponent) } // Kotlin 可以直接通过扩展函数根据类型寻找目标 val fooComponent = app.components.find<FooComponent>() println(fooComponent)
Applications.launchApplicationAsync(Simple.INSTANCE, configurer -> { // 注册 configurer.install(FooComponent.FACTORY); }).asFuture().thenAccept(app -> { // 寻找注册的组件中的 FooComponent // 也可以使用类型寻找, 此处图省事儿, 直接通过id寻找 var fooComponent = app.getComponents().findById(FooComponent.ID_VALUE); System.out.println(fooComponent); });
var app = Applications.launchApplicationBlocking(Simple.INSTANCE, configurer -> { configurer.install(FooComponent.FACTORY); }); // 也可以使用类型寻找, 此处图省事儿, 直接通过id寻找 var fooComponent = app.getComponents().findById(FooComponent.ID_VALUE); System.out.println(fooComponent);
Last modified: 19 September 2024