Simple Robot v4.10.0 Help

拦截器 Interceptor

在构建 Application 的过程中,可以通过 eventDispatcher 作用域来配置事件调度器, 而在其中可以为整个调度器增加拦截器。

拦截器分为两种,一个是 全局拦截器 (EventDispatchInterceptor ),一个是 局部拦截器 (EventInterceptor), 它们的区别在于它们生效的范围。

种类

范围

全局拦截器

整个事件调度的过程,其中可能包含多个事件处理器(以及它们可能包含的多个 局部拦截器)。

局部拦截器

每一个事件处理器的处理流程。

全局拦截器

放行:

val app = launchSimpleApplication { eventDispatcher { // 添加一个全局拦截器 addDispatchInterceptor { // 放行、或进入下一个拦截 invoke() } } }

拦截并返回自定义结果:

val app = launchSimpleApplication { eventDispatcher { addDispatchInterceptor { // 这里直接返回了自定义的 `Flow<EventResult` 而没有执行 `invoke`, // 因此实际上没有任何事件处理器被执行, // 也就是被"拦截"了。 flowOf(EventResult.empty(), EventResult.empty()) } } }

局部拦截器

放行:

val app: Application = ... app.listeners { process<Event>(propertiesConsumer = { // 通过 propertiesConsumer 配置事件处理器的属性, // 这其中可以通过 addInterceptor 添加针对当前事件处理器的局部拦截器。 addInterceptor { // 放行、或进入下一个拦截 invoke() } }) { event -> println("Event: $event") } }

拦截并返回自定义结果:

val app: Application = ... app.listeners { process<Event>(propertiesConsumer = { // 通过 propertiesConsumer 配置事件处理器的属性, // 这其中可以通过 addInterceptor 添加针对当前事件处理器的局部拦截器。 addInterceptor { // 这里直接返回了自定义的 `EventResult` 而没有执行 `invoke`, // 因此实际上事件处理器没有被执行, // 也就是被"拦截"了。 EventResult.empty() } }) { event -> println("Event: $event") } }

Quantcat (注解API) 支持

Quantcat API 中定义了与拦截器相关的注解: @Interceptor, 因此在支持 Quantcat API 的地方 (比如 Spring Boot 中) 也支持使用注解来注册拦截器。

@ContentTrim 为例,我们介绍一下如何实现一个自定义的拦截器, 使其功能与 @ContentTrim 一样,并通过 @Interceptor 使用在某个监听函数上。

首先,我们先来看看 @Interceptor 的定义:

public annotation class Interceptor( /** * 提供一个 [AnnotationEventInterceptorFactory] 的 **实现类型**。 * 如果此类型是 `object`,则直接使用,否则会构建一个实例。 * 此实例 **可能会被共享**,因此请考虑处理并发。 * * 如果希望添加多个拦截器,请使用多个 [Interceptor] 注解。 */ val value: KClass<out AnnotationEventInterceptorFactory>, /** * 提供给 [AnnotationEventInterceptorFactory] 的预期注册优先级。 */ val priority: Int = PriorityConstant.DEFAULT )

可以看到,它有两个参数:一个用来构建拦截器的工厂类型 value, 和一个优先级参数 priority

优先级我想它的含义不需要赘述,因此我们着重来讲一下 value 。它需要一个 可以被实例化AnnotationEventInterceptorFactory 类型的 KClass (Java中即为 Class), 这个类型就需要我们来自定义了。

接下来,实现一个 MyContentTrimAnnotationEventInterceptorFactory

public data object ContentTrimEventInterceptorFactory : AnnotationEventInterceptorFactory() { override fun create(context: AnnotationEventInterceptorFactory.Context): AnnotationEventInterceptorFactory.Result { TODO("待实现...") } }
public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory { @Override public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) { // TODO 待实现... return null; } }

工厂准备好了,接下来我们需要实现一个 拦截器(EventInterceptor) ,并在其中实现我们的功能。

public data object ContentTrimEventInterceptorFactory : AnnotationEventInterceptorFactory() { override fun create(context: AnnotationEventInterceptorFactory.Context): AnnotationEventInterceptorFactory.Result { TODO("待实现...") } private data object InterceptorImpl : EventInterceptor { override suspend fun EventInterceptor.Context.intercept(): EventResult { with(eventListenerContext) { plainText = plainText?.trim() } return invoke() } } }

Java 中,由于存在挂起函数,你无法直接实现 EventInterceptor。 根据异步和阻塞的不同,你可以选择不同的Java衍生类型实现。


public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory { @Override public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) { // TODO return null; } private static final class InterceptorImpl implements JAsyncEventInterceptor { @Override public @NotNull CompletableFuture<EventResult> intercept(@NotNull JAsyncEventInterceptor.Context context) throws Exception { final var eventListenerContext = context.getSource().getEventListenerContext(); final var plainText = eventListenerContext.getPlainText(); if (plainText != null) { eventListenerContext.setPlainText(plainText.trim()); } // 放行 return context.invoke(); } } }
public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory { @Override public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) { // TODO return null; } private static final class InterceptorImpl implements JBlockEventInterceptor { @Override public @NotNull EventResult intercept(@NotNull JBlockEventInterceptor.Context context) throws Exception { final var eventListenerContext = context.getSource().getEventListenerContext(); final var plainText = eventListenerContext.getPlainText(); if (plainText != null) { eventListenerContext.setPlainText(plainText.trim()); } // 放行 return context.invoke(); } } }

实现完了拦截器,则最后一步便是在工厂中返回你的拦截器实例。

public data object ContentTrimEventInterceptorFactory : AnnotationEventInterceptorFactory() { override fun create(context: AnnotationEventInterceptorFactory.Context): AnnotationEventInterceptorFactory.Result { return AnnotationEventInterceptorFactory.Result.build { // 拦截器实例 interceptor(InterceptorImpl) // 针对拦截器的一些额外配置... // 比如优先级, 优先级可以使用来自注解的配置 configuration { priority = context.priority } } } private data object InterceptorImpl : EventInterceptor { // 内容省略... } }
public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory { @Override public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) { return Result.build(config -> { // 拦截器实例 config.interceptor(new InterceptorImpl()); config.configuration(interceptorProperties -> { // 针对拦截器的一些额外配置... // 比如优先级, 优先级可以使用来自注解的配置 interceptorProperties.setPriority(context.getPriority()); }); }); } private static final class InterceptorImpl implements ... { // 内容省略... } }

可以看到,我们使用 AnnotationEventInterceptorFactory.Result.build 构建了一个 AnnotationEventInterceptorFactory.Result 并返回。

它的内容便是本次构建的拦截器以及其配置。如果此时返回 null 则代表不添加拦截器。

实现完了工厂,我们就可以在标记了 @Listener 的监听上使用它了。

@Listener @Interceptor(MyContentTrimAnnotationEventInterceptorFactory::class) suspend fun handle(event: Event) { // ... }
@Listener @Interceptor(MyContentTrimAnnotationEventInterceptorFactory.class) public void handle(Event event) { // ... }

你也可以像 @ContentTrim 一样,使用一个自定义注解来包装它。

@Target(AnnotationTarget.FUNCTION) @Interceptor(MyContentTrimAnnotationEventInterceptorFactory::class) public annotation class MyContentTrim
@Listener @MyContentTrim suspend fun handle(event: Event) { // ... }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Interceptor(MyContentTrimAnnotationEventInterceptorFactory.class) public @interface MyContentTrim {}
@Listener @MyContentTrim public void handle(Event event) { // ... }
Last modified: 18 January 2025