Skip to content

Commit e46fcc3

Browse files
committed
拦截器自定义注解相关文档
1 parent 78b112e commit e46fcc3

File tree

1 file changed

+277
-1
lines changed

1 file changed

+277
-1
lines changed

Writerside/topics/advanced-interceptor.md

Lines changed: 277 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,296 @@ app.listeners {
9595
[Quantcat API](advanced-quantcat.md) 中定义了与拦截器相关的注解:`@Interceptor`
9696
因此在支持 Quantcat API 的地方 (比如 Spring Boot 中) 也支持使用注解来注册拦截器。
9797

98+
<note>
99+
98100
在 Quantcat API 中内置了一个很常用的注解 `@ContentTrim`, 它便是基于 `@Interceptor` 的一个衍生注解,
99101
可以用于对标记的监听函数 (标记了 `@Listener` 的函数) 添加一个**局部拦截器**
100102

103+
</note>
104+
105+
`@ContentTrim` 为例,我们介绍一下如何实现一个自定义的拦截器,
106+
使其功能与 `@ContentTrim` 一样,并通过 `@Interceptor` 使用在某个监听函数上。
107+
108+
首先,我们先来看看 `@Interceptor` 的定义:
109+
110+
```kotlin
111+
public annotation class Interceptor(
112+
/**
113+
* 提供一个 [AnnotationEventInterceptorFactory] 的 **实现类型**。
114+
* 如果此类型是 `object`,则直接使用,否则会构建一个实例。
115+
* 此实例 **可能会被共享**,因此请考虑处理并发。
116+
*
117+
* 如果希望添加多个拦截器,请使用多个 [Interceptor] 注解。
118+
*/
119+
val value: KClass<out AnnotationEventInterceptorFactory>,
120+
121+
/**
122+
* 提供给 [AnnotationEventInterceptorFactory] 的预期注册优先级。
123+
*/
124+
val priority: Int = PriorityConstant.DEFAULT
125+
)
126+
```
127+
128+
可以看到,它有两个参数:一个用来构建拦截器的工厂类型 `value`, 和一个优先级参数 `priority`
129+
130+
优先级我想它的含义不需要赘述,因此我们着重来讲一下 `value`。它需要一个 **可以被实例化**`AnnotationEventInterceptorFactory` 类型的 `KClass` (Java中即为 `Class`),
131+
这个类型就需要我们来自定义了。
132+
133+
接下来,实现一个 `MyContentTrimAnnotationEventInterceptorFactory`
134+
135+
<tabs group="code">
136+
<tab title="Kotlin" group-key="Kotlin">
137+
138+
```kotlin
139+
public data object ContentTrimEventInterceptorFactory : AnnotationEventInterceptorFactory() {
140+
override fun create(context: AnnotationEventInterceptorFactory.Context): AnnotationEventInterceptorFactory.Result {
141+
TODO("待实现...")
142+
}
143+
}
144+
```
145+
146+
<note>
147+
148+
在 Kotlin 中,可以直接使用 `object`
149+
150+
</note>
151+
152+
</tab>
153+
<tab title="Java" group-key="Java">
154+
155+
```java
156+
public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory {
157+
@Override
158+
public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) {
159+
// TODO 待实现...
160+
return null;
161+
}
162+
}
163+
```
164+
165+
</tab>
166+
</tabs>
167+
168+
工厂准备好了,接下来我们需要实现一个 **拦截器(`EventInterceptor`)** ,并在其中实现我们的功能。
169+
170+
> 拦截器的实现类型不需要被外界感知,此处为了方便,直接作为上述工厂类型的内部类了。自己实现的时候根据实际情况自行选择实现方式即可。
171+
101172
<tabs group="code">
102173
<tab title="Kotlin" group-key="Kotlin">
103174

175+
```Kotlin
176+
public data object ContentTrimEventInterceptorFactory : AnnotationEventInterceptorFactory() {
177+
override fun create(context: AnnotationEventInterceptorFactory.Context): AnnotationEventInterceptorFactory.Result {
178+
TODO("待实现...")
179+
}
180+
181+
private data object InterceptorImpl : EventInterceptor {
182+
override suspend fun EventInterceptor.Context.intercept(): EventResult {
183+
with(eventListenerContext) {
184+
plainText = plainText?.trim()
185+
}
104186

187+
return invoke()
188+
}
189+
}
190+
}
191+
```
105192

106193
</tab>
107194
<tab title="Java" group-key="Java">
108195

196+
Java 中,由于存在挂起函数,你无法直接实现 `EventInterceptor`
197+
根据异步和阻塞的不同,你可以选择不同的Java衍生类型实现。
198+
199+
> 通过文档右上角可以切换展示的API风格。
200+
201+
<p switcher-key="%ja%">
202+
203+
使用 `JAsyncEventInterceptor` 可使用异步API实现 `EventInterceptor`
204+
对于性能来讲,其优于阻塞API。
205+
206+
</p>
207+
208+
<br/>
209+
210+
```Java
211+
public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory {
212+
@Override
213+
public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) {
214+
// TODO
215+
return null;
216+
}
217+
218+
private static final class InterceptorImpl implements JAsyncEventInterceptor {
219+
@Override
220+
public @NotNull CompletableFuture<EventResult> intercept(@NotNull JAsyncEventInterceptor.Context context) throws Exception {
221+
final var eventListenerContext = context.getSource().getEventListenerContext();
222+
final var plainText = eventListenerContext.getPlainText();
223+
if (plainText != null) {
224+
eventListenerContext.setPlainText(plainText.trim());
225+
}
109226

227+
// 放行
228+
return context.invoke();
229+
}
230+
}
231+
}
232+
```
233+
{switcher-key="%ja%"}
234+
235+
<p switcher-key="%jb%">
236+
237+
使用 `JBlockEventInterceptor` 可使用阻塞API实现 `EventInterceptor`
238+
对于一些复杂的逻辑,它可能会更简单一些。
239+
240+
</p>
241+
242+
```Java
243+
public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory {
244+
@Override
245+
public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) {
246+
// TODO
247+
return null;
248+
}
249+
250+
private static final class InterceptorImpl implements JBlockEventInterceptor {
251+
@Override
252+
public @NotNull EventResult intercept(@NotNull JBlockEventInterceptor.Context context) throws Exception {
253+
final var eventListenerContext = context.getSource().getEventListenerContext();
254+
final var plainText = eventListenerContext.getPlainText();
255+
if (plainText != null) {
256+
eventListenerContext.setPlainText(plainText.trim());
257+
}
258+
259+
// 放行
260+
return context.invoke();
261+
}
262+
}
263+
}
264+
```
265+
{switcher-key="%jb%"}
110266

111267
</tab>
112268
</tabs>
113269

114-
TODO
270+
实现完了拦截器,则最后一步便是在工厂中返回你的拦截器实例。
271+
272+
<tabs group="code">
273+
<tab title="Kotlin" group-key="Kotlin">
274+
275+
```Kotlin
276+
public data object ContentTrimEventInterceptorFactory : AnnotationEventInterceptorFactory() {
277+
override fun create(context: AnnotationEventInterceptorFactory.Context): AnnotationEventInterceptorFactory.Result {
278+
return AnnotationEventInterceptorFactory.Result.build {
279+
// 拦截器实例
280+
interceptor(InterceptorImpl)
281+
// 针对拦截器的一些额外配置...
282+
// 比如优先级, 优先级可以使用来自注解的配置
283+
configuration { priority = context.priority }
284+
}
285+
}
286+
287+
private data object InterceptorImpl : EventInterceptor {
288+
// 内容省略...
289+
}
290+
}
291+
```
292+
293+
</tab>
294+
<tab title="Java" group-key="Java">
295+
296+
```Java
297+
public class MyContentTrimAnnotationEventInterceptorFactory implements AnnotationEventInterceptorFactory {
298+
@Override
299+
public @Nullable Result create(@NotNull AnnotationEventInterceptorFactory.Context context) {
300+
return Result.build(config -> {
301+
// 拦截器实例
302+
config.interceptor(new InterceptorImpl());
303+
config.configuration(interceptorProperties -> {
304+
// 针对拦截器的一些额外配置...
305+
// 比如优先级, 优先级可以使用来自注解的配置
306+
interceptorProperties.setPriority(context.getPriority());
307+
});
308+
});
309+
}
310+
311+
private static final class InterceptorImpl implements ... {
312+
// 内容省略...
313+
}
314+
}
315+
```
316+
317+
</tab>
318+
</tabs>
319+
320+
可以看到,我们使用 `AnnotationEventInterceptorFactory.Result.build`
321+
构建了一个 `AnnotationEventInterceptorFactory.Result` 并返回。
322+
323+
它的内容便是本次构建的拦截器以及其配置。如果此时返回 `null` 则代表不添加拦截器。
324+
325+
实现完了工厂,我们就可以在标记了 `@Listener` 的监听上使用它了。
326+
327+
<tabs group="code">
328+
<tab title="Kotlin" group-key="Kotlin">
329+
330+
```Kotlin
331+
@Listener
332+
@Interceptor(MyContentTrimAnnotationEventInterceptorFactory::class)
333+
suspend fun handle(event: Event) {
334+
// ...
335+
}
336+
```
337+
338+
</tab>
339+
<tab title="Java" group-key="Java">
340+
341+
```Java
342+
@Listener
343+
@Interceptor(MyContentTrimAnnotationEventInterceptorFactory.class)
344+
public void handle(Event event) {
345+
// ...
346+
}
347+
```
348+
349+
</tab>
350+
</tabs>
351+
352+
你也可以像 `@ContentTrim` 一样,使用一个自定义注解来包装它。
353+
354+
<tabs group="code">
355+
<tab title="Kotlin" group-key="Kotlin">
356+
357+
```Kotlin
358+
@Target(AnnotationTarget.FUNCTION)
359+
@Interceptor(MyContentTrimAnnotationEventInterceptorFactory::class)
360+
public annotation class MyContentTrim
361+
```
362+
363+
```Kotlin
364+
@Listener
365+
@MyContentTrim
366+
suspend fun handle(event: Event) {
367+
// ...
368+
}
369+
```
370+
371+
</tab>
372+
<tab title="Java" group-key="Java">
373+
374+
```Java
375+
@Target(ElementType.METHOD)
376+
@Retention(RetentionPolicy.RUNTIME)
377+
@Interceptor(MyContentTrimAnnotationEventInterceptorFactory.class)
378+
public @interface MyContentTrim {}
379+
```
380+
381+
```Java
382+
@Listener
383+
@MyContentTrim
384+
public void handle(Event event) {
385+
// ...
386+
}
387+
```
388+
389+
</tab>
390+
</tabs>

0 commit comments

Comments
 (0)