🐬 SSE 拦截器
# SSE 拦截器
SSE 拦截器也是普通拦截器的一种,实现自定义的 SSE 拦截器需要实现com.dtflys.forest.interceptor.SSEInterceptor
接口。
如果还不了解什么是拦截器和Interceptor
接口,可以先参见《拦截器》。
相比于自定义 SSE 控制器,SSE 拦截器同样可以控制 SSE 事件流的各个生命周期、以及处理所有事件消息,唯一有所不同的是,SSE 拦截器是单例
,不会像 SSE 控制器那样每发起一次 SSE 请求都会实例化一个独立的对象,一个 SSE 拦截器类永远都只有一个实例。
// 自定义 SSE 拦截器,需实现 SSEInterceptor 接口
public class MySSEInterceptor implements SSEInterceptor {
// onSuccess 方法可重写,可不重写
@Override
public void onSuccess(InputStream data, ForestRequest request, ForestResponse response) {
// 和普通拦截器 onSuccess 相同,data 是 SSE 的消息流,不建议在这里动它
}
// afterExecute 方法可重写,可不重写
@Override
public void afterExecute(ForestRequest request, ForestResponse response) {
// 和普通拦截器 afterExecute 相同
}
// onSSEOpen 方法可重写,可不重写
@Override
public void onSSEOpen(EventSource eventSource) {
// SSE 开始监听时调用
}
// onSSEClose 方法可重写,可不重写
@Override
public void onSSEClose(ForestRequest request, ForestResponse response) {
// SSE 结束监听时调用
}
// 1.6.4+ 版本可以重写 onMessage 方法来接收 SSE 消息
@Override
public void onMessage(EventSource event) {
// 接收到 SSE 消息时调用
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
由上面代码可见,SSE 拦截器也是普通拦截器接口Interceptor
的实现类,而相比于普通拦截器而言,SSE 拦截器多了onSSEOpen
和onSSEClose
方法可重写,分别用于在 SSE 开始监听和结束监听时调用。
此外,和自定义 SSE 控制器一样,可以在 SSE 拦截器中也可以声明和定义 SSE 事件处理方法
// 自定义 SSE 拦截器,需实现 SSEInterceptor 接口
public class MySSEInterceptor implements SSEInterceptor {
// onSSEOpen 方法可重写,可不重写
@Override
public void onSSEOpen(EventSource eventSource) {
// SSE 开始监听时调用
}
// onSSEClose 方法可重写,可不重写
@Override
public void onSSEClose(ForestRequest request, ForestResponse response) {
// SSE 结束监听时调用
}
// 1.6.4+ 版本可以重写 onMessage 方法来接收 SSE 消息
@Override
public void onMessage(EventSource event) {
event.id(); // 获取消息名称为 id 的消息值
event.event(); // 获取消息名称为 event 的消息值
event.data(); // 获取消息名称为 data 的消息值
event.retry(); // 获取消息名称为 retry 的消息值
event.value("text"); // 获取非标准消息名称为 text 的消息值
}
// 只处理消息名为 data 的事件
@SSEDataMessage
public void onSSEData(EventSource eventSource, @SSEName String name, @SSEValue String value) {
// 处理事件消息名称为 data 的事件
// 声明的方法名和处理的事件消息名称没有关系,方法名可以是 onData、onSseData、onMessage、xxx、以及任何名称都可以
// @SSEName 注解修饰的参数 name 为事件消息名称 (同 eventSource.name())
// @SSEValue 注解修饰的参数 value 为事件消息的值
eventSource.sse(); // 获取 SSE 控制器
eventSource.request(); // 获取 Forest 请求对象
eventSource.response(); // 获取 Forest 响应对象
eventSource.rawData(); // 获取事件消息的原始数据
eventSource.name(); // 获取事件消息的名称,如 data、event、id 等
eventSource.value(); // 获取事件消息的值,返回字符串类型数据
// 获取事件消息的值,并将消息转换为指定的自定义类型
eventSource.value(MyUser.class);
// 获取事件消息的值,并将消息转换为指定的自定义泛型类型
eventSource.value(new TypeReference<List<MyUser>>() {});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
关于具体如何声明和定义 SSE 事件消息的处理方法,请参见《SSE事件处理方法》
# SSE 拦截器与 Spring 集成
SSE 控制器和自定义 SSE 控制器都是非单例的独立实例对象,所以无法直接注入和使用 spring 上下文中的资源。
而 SSE 拦截器接口SSEInterceptor
继承自Interceptor
,可以注入到 spring 上下文中并使用其中的 bean,但和普通拦截器一样,一定要加上@Component
注解,或者确保它一定被 Spring 扫描器扫描到
// 要注入 Spring 上下文的,一定要加上 @Component 注解,不加上就不能注入到 Spring 上下文
@Component
public class MySSEInterceptor implements SSEInterceptor {
// 可以注入 Spring 上下文中的对象
@Resource
private MyUserService myUserService;
}
2
3
4
5
6
7
8
9
# 在 SSE 拦截器中传递数据
注意
不能使用共享变量
由于 SSE 拦截器是单例,所以不能像 SSE 控制器那样使用共享变量的方式来传递数据
而应该像普通拦截器那样使用Attribute
或Attachment
详情请参见《在拦截器中传递数据》
使用Attribute
或Attachment
来实现跨生命周期、跨事件消息的数据传递
// 自定义 SSE 拦截器,需实现 SSEInterceptor 接口
public class MySSEInterceptor implements SSEInterceptor {
@Override
public void onSuccess(InputStream data, ForestRequest request, ForestResponse response) {
// 请求发送成功是调用
// 创建或获取名为 text 的 Attachment,类型为 StringBuilder
StringBuilder builder = (StringBuilder) request.getOrAddAttachment("text", StringBuilder::new);
builder.append("onSuccess\n");
}
@Override
public void afterExecute(ForestRequest request, ForestResponse response) {
// 请求执行完成后调用
// 创建或获取名为 text 的 Attachment,类型为 StringBuilder
StringBuilder builder = (StringBuilder) request.getOrAddAttachment("text", StringBuilder::new);
builder.append("afterExecute\n");
}
@SSEDataMessage
public void onData(ForestRequest request, @SSEName String name, @SSEValue String value) {
// 处理事件消息名称为 data 的事件
// 创建或获取名为 text 的 Attachment,类型为 StringBuilder
StringBuilder builder = (StringBuilder) request.getOrAddAttachment("text", StringBuilder::new);
builder.append("Receive name=" + name + "; value=" + value + "\n");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28