# Application

> For the complete documentation index, see [llms.txt](https://simbot.forte.love/llms.txt).

`Application` 是对一组注册的组件和插件们的统一运行环境。 组件和插件们总要依附在一个 `Application` 上。

`simbot-core` 模块提供了一个简单的普通实现：`SimpleApplication`, 下文将会以这个简单的普通实现为例进行说明。

> **Tip:**
> Spring Boot starter 模块 `simbot-core-spring-boot-starter` 有另外一个类型的实现, 不过它大部分内容也是基于 `SimpleApplication`, 且不需要也不应手动构造, 所以不做介绍。

## 创建与启动

Kotlin:

在 Kotlin 中, 使用 `launchApplication(Factory) { config... }` 配置并启动一个 `Application`。

其中, `Factory` 是 `Application` 的工厂实例, 在 `simbot-core` 中它是 `Simple`。

```KOTLIN
val app = launchApplication(Simple) {
    config {
        // 一些 Application 的配置, 例如协程上下文
        coroutineContext = Dispatchers.Default
    }

    // 一些事件调度器的配置, 例如协程上下文、拦截器等
    eventDispatcher {
        coroutineContext = Dispatchers.Default
        addInterceptor { `this` ->
            // intercept..
            `this`.invoke() // invoke
        }
        addDispatchInterceptor { `this` ->
            // intercept..
            `this`.invoke() // invoke
        }
    }

    // 安装组件或插件
    install(...)

    // 安装组件或插件, 以及额外的配置
    install(...) {
        // ...
    }
}
```

`Simple` 实现提供一个简写扩展函数：`launchSimpleApplication`：

```KOTLIN
val app = launchSimpleApplication {
    // ...
}
```

Java:

在 Java 中, 使用 `Applications.launchApplicationXxx(Factory) { config... }` 配置并启动一个 `Application`。

其中, `Factory` 是 `Application` 的工厂实例, 在 `simbot-core` 中它是 `Simple.INSTANCE`。

```JAVA
var applicationAsync = Applications.launchApplicationAsync(Simple.INSTANCE, appConfigurer -> {
    // Application 的配置信息
    appConfigurer.config(appConfig -> {
        // 比如配置协程上下文
        // 此处仅做示例：配置一个自定义的线程池到上下文中。
        // 并不建议非常随意的使用 Executors 中的内容。再强调一遍, 此处仅作为示例。
        appConfig.setCoroutineContext(ExecutorsKt.from(Executors.newCachedThreadPool()));
    });


    // 事件调度器的配置信息
    appConfigurer.eventDispatcher(dispatcherConfig -> {
        // 例如可以配置协程上下文、添加拦截器之类的。
        // 配置协程上下文上面有示例的, 此处就不再重复了, 是类似的效果。

        // 添加一个事件处理拦截器
        dispatcherConfig.addInterceptor(EventInterceptors.async(context -> {
            // 拦截...
            // 假设：重新设置事件处理过程中的 `plainText`, 去除其前后空白字符
            var listenerContext = context.getSource().getEventListenerContext();
            var plainText = listenerContext.getPlainText();
            listenerContext.setPlainText(plainText == null ? null : plainText.trim());

            return context.invoke();
        }));

        // 安装组件、插件, 以及可选的进行配置
        appConfigurer.install(...);
        appConfigurer.install(..., config -> {
            // 配置...
        });
    });
});

// 异步结果可转化为 CompletableFuture
var future = applicationAsync.asFuture();
// ...

```

```JAVA
var application = Applications.launchApplicationBlocking(Simple.INSTANCE, appConfigurer -> {
    // Application 的配置信息
    appConfigurer.config(appConfig -> {
        // 比如配置协程上下文
        // 此处仅做示例：配置一个自定义的线程池到上下文中。
        // 并不建议非常随意的使用 Executors 中的内容。再强调一遍, 此处仅作为示例。
        appConfig.setCoroutineContext(ExecutorsKt.from(Executors.newCachedThreadPool()));
    });


    // 事件调度器的配置信息
    appConfigurer.eventDispatcher(dispatcherConfig -> {
        // 例如可以配置协程上下文、添加拦截器之类的。
        // 配置协程上下文上面有示例的, 此处就不再重复了, 是类似的效果。

        // 添加一个事件处理拦截器
        dispatcherConfig.addInterceptor(EventInterceptors.block(context -> {
            // 拦截...
            // 假设：重新设置事件处理过程中的 `plainText`, 去除其前后空白字符
            var listenerContext = context.getSource().getEventListenerContext();
            var plainText = listenerContext.getPlainText();
            listenerContext.setPlainText(plainText == null ? null : plainText.trim());

            return context.invoke();
        }));

        // 安装组件、插件, 以及可选的进行配置
        appConfigurer.install(...);
        appConfigurer.install(...,config -> {
            // 配置...
        });
    });
});

// ...
```

```JAVA
var applicationAsync = Applications.launchApplicationAsync(Simple.INSTANCE, appConfigurer -> {
    appConfigurer.config(appConfig -> {
        appConfig.setCoroutineContext(ExecutorsKt.from(Executors.newCachedThreadPool()));
    });

    appConfigurer.eventDispatcher(dispatcherConfig -> {
        dispatcherConfig.addInterceptor(EventInterceptors.async(context -> {
            var listenerContext = context.getSource().getEventListenerContext();
            var plainText = listenerContext.getPlainText();
            listenerContext.setPlainText(plainText == null ? null : plainText.trim());

            return context.invoke();
        }));

        appConfigurer.install(...);
        appConfigurer.install(..., config -> {
            // 配置...
        });
    });
});

Mono.fromFuture(applicationAsync.asFuture())
        .flatMap(app -> app.joinReserve().transform(SuspendReserves.mono()))
        .block();
```

构建完 `Application` 便可以开始注册事件处理器和 `Bot` 了。

> **Note:**
> `Application.join()` 在 JVM 上除了 `joinBlocking()` 以外， 异步桥接名称是 `asFuture()`，而不是 `joinAsync()`。

## 取消与等待结束

调用 `application.cancel()` 或 `application.cancel(reason)` 终止应用。 核心库与 Spring Boot starter 的实现依次触发：

1. `ApplicationLaunchStage.RequestCancel`：在取消应用的 `Job` 前执行。

2. 取消应用的 `Job`，`isActive` 变为 `false`。

3. `ApplicationLaunchStage.Cancelled`：在发起任务取消后、`cancel()` 返回前执行。

阶段处理器应快速完成，避免长时间阻塞。直接取消 `coroutineContext` 中的 `Job` 不会触发这些阶段处理器。 `Cancelled` 触发时子任务可能仍在结束过程中；等待应用完全结束使用 `join()`， 也可以通过 `isCompleted` 或 `onCompletion` 感知完成状态。

```KOTLIN
application.cancel()
application.join()
```

## 事件处理器的注册

有关事件处理器的相关描述可前往 [事件监听与处理](basic-event-listener.html) 章节阅读。

## Bot的注册

有关Bot注册的相关描述可前往 [Bot管理器](botmanager.html) 章节阅读。

