通过注解方式实现大屏数据的动态推送:Java/SpringBoot 实战指南
本文提出了一种基于注解的动态数据推送方案,用于解决大屏项目中推送逻辑分散、耦合度高的问题。通过定义@ScreenPush注解和AOP切面,业务方法只需添加注解即可自动完成数据序列化和推送,无需手动调用推送接口。方案支持异步推送、定时任务、多通道推送等功能,实现了业务逻辑与推送逻辑的解耦,提高了代码复用性和可维护性。核心组件包括注解定义、AOP拦截、推送服务和序列化模块,可灵活适配WebSocket
在大屏项目中,数据往往需要“实时、动态、自动”推送给前端页面。常见方式包括 WebSocket、SSE(Server-Sent Events)、长轮询等。大多数项目会手动写 push 逻辑,但随着模块增多,会出现:代码分散、推送逻辑耦合、模板化代码重复等问题。
本文介绍一种优雅的方式 → 使用注解(Annotation)实现大屏数据的动态推送:业务方法只需要标注注解,返回值即可自动推送到大屏。支持模块化、多通道、自动序列化、动态监听、多大屏 ID 推送。
一、为什么用注解?
传统写法:
public void queryOrder() {
Result r = service.getOrderCount();
webSocket.send("order", r);
}
问题:
-
所有模块都要手动调用 webSocket
-
大屏越多,入口越多,代码越乱
-
推送逻辑与业务逻辑强耦合
改用注解后:
@ScreenPush("order")
public Result queryOrder() {
return service.getOrderCount();
}
业务方只写业务逻辑,推送框架自动完成:
-
采集数据
-
序列化成 JSON
-
根据不同 screenId 群发
-
异步推送
你甚至可以定时执行,自动刷新大屏。
二、整体架构设计
核心组件:
-
注解定义:
@ScreenPush -
切面(AOP)自动拦截标注方法
-
推送服务(WebSocket / SSE)
-
序列化组件(Jackson/fastjson)
-
大屏 ID 管理(多大屏推送)
架构图:
业务方法 → 注解 → AOP → 推送组件 → 大屏前端(WebSocket/SSE)
三、注解定义
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ScreenPush {
/** 推送的事件名,比如 order/user/chart1 */
String value();
/** 是否异步推送 */
boolean async() default true;
}
四、切面(AOP)拦截标注方法
核心逻辑:
-
拦截方法执行
-
执行原始业务逻辑,获得返回值
-
自动序列化成 JSON
-
自动推送到指定的大屏事件通道
@Aspect
@Component
public class ScreenPushAspect {
@Autowired
private ScreenPusher screenPusher; // 推送核心组件
@Around("@annotation(screenPush)")
public Object around(ProceedingJoinPoint pjp, ScreenPush screenPush) throws Throwable {
// 执行业务方法
Object result = pjp.proceed();
// 将结果推送到大屏
if (screenPush.async()) {
CompletableFuture.runAsync(() -> screenPusher.push(screenPush.value(), result));
} else {
screenPusher.push(screenPush.value(), result);
}
return result;
}
}
五、推送组件(以 WebSocket 为示例)
支持广播给所有大屏,也支持按 screenId 推送。
@Component
public class ScreenPusher {
@Autowired
private WebSocketServer webSocketServer; // 业务自己的 WS 组件
public void push(String event, Object data) {
String json = JSON.toJSONString(data);
webSocketServer.broadcast(event, json);
}
}
六、WebSocket 服务端示例
@Component
@ServerEndpoint("/ws/screen/{screenId}")
public class WebSocketServer {
private static final Map<String, Session> SESSION_MAP = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("screenId") String screenId) {
SESSION_MAP.put(screenId, session);
}
@OnClose
public void onClose(@PathParam("screenId") String screenId) {
SESSION_MAP.remove(screenId);
}
public void broadcast(String event, String json) {
SESSION_MAP.values().forEach(session -> {
try {
session.getBasicRemote().sendText(buildMessage(event, json));
} catch (Exception ignored) {}
});
}
private String buildMessage(String event, String json) {
return "{" + "\"event\":\"" + event + "\"," + "\"data\":" + json + "}";
}
}
七、业务层如何使用?(最简单的体验)
只需要加注解即可!
@ScreenPush("order")
public OrderDTO loadOrderInfo() {
return orderService.getOrderSummary();
}
调用此方法时,大屏自动收到:
{
"event": "order",
"data": {
"todayCount": 123,
"totalAmount": 88888
}
}
八、支持定时任务 + 注解自动推送(大屏常用)
@Scheduled(cron = "0/10 * * * * ?")
@ScreenPush("pv")
public PvDTO calcPvUv() {
return pvService.statPvUv();
}
每 10 秒自动推送数据,无需任何额外代码。
九、支持多大屏 ID 推送(可选增强版)
注解增强:
String[] screenIds() default {}; // 空默认推所有
切面根据 screenIds 精准推送。
十、优点总结
✔ 业务与推送解耦
业务不再需要接触 WebSocket / SSE。
✔ 推送逻辑统一维护
减少重复代码。
✔ 易扩展,多推送方式可切换
WebSocket → SSE → Kafka → RedisPubSub。
✔ 支持多大屏 ID
适合多大屏同时在线。
✔ 支持定时任务自动推
非常适合大屏定时刷新。
更多推荐
所有评论(0)