实现组件标识
组件标识 是实现一个组件的基础。
实现组件标识的基本步骤
实现接口 Component
。
如果需要的话, 实现一个配置类。
实现 ComponentFactory
来提供工厂。
可选地支持 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
提供一切可能需要用到的序列化信息, 例如 SerializableBotConfiguration
或 Message.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: 15 November 2024