@@ -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