实现实时更新
在 Spring Boot 中开发订阅解析器,发布事件并向客户端发送实时数据。
实现实时更新 是 CoddyKit 上的免费 GraphQL APIs with Spring Boot 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 GraphQL APIs with Spring Boot 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 GraphQL APIs with Spring Boot 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Subscription Resolvers Explained
Subscriptions deliver real-time updates. Unlike queries that return data once, subscription resolvers return a stream of data that continuously pushes updates to clients.
We'll learn how to implement these streams in a Spring Boot GraphQL application.
Embracing Reactor Flux
Spring GraphQL leverages Project Reactor's Flux to handle subscriptions. A Flux represents an asynchronous, non-blocking stream of 0 to N items.
- It's ideal for continuous data delivery.
- You can emit multiple values over time.
Schema for Real-time Updates
First, we define our subscription in the GraphQL Schema Definition Language (SDL). This example shows a subscription for new messages:
type Subscription {
messageAdded(channelId: ID!): Message
}
type Message {
id: ID!
text: String!
channelId: ID!
timestamp: String!
}Creating a Subscription Resolver
In Spring Boot, a subscription resolver is a method annotated with @SubscriptionMapping. It must return a Flux of the desired type.
We'll use a Sinks.Many to manage and publish events into this stream.
import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
import org.springframework.stereotype.Controller;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
@Controller
public class MessageSubscriptionController {
private final Sinks.Many<Message> messageSink =
Sinks.many().multicast().onBackpressureBuffer();
@SubscriptionMapping
public Flux<Message> messageAdded() {
return messageSink.asFlux();
}
}Publishing Events with Sinks.Many
The Sinks.Many instance acts as our event publisher. When an event occurs (e.g., a new message is created), we use its tryEmitNext() method to send data into the stream.
This data then flows through the Flux to all connected GraphQL subscribers.
public class MessageService {
private final Sinks.Many<Message> messageSink;
public MessageService(Sinks.Many<Message> messageSink) {
this.messageSink = messageSink;
}
public Message createMessage(String text, String channelId) {
// ... save message to DB ...
Message newMessage = new Message("1", text, channelId, "now");
messageSink.tryEmitNext(newMessage); // Publish the new message
return newMessage;
}
}Full Runnable Example Setup
Let's build a complete, runnable Spring Boot application. We'll define a Message record and a MessagePublisher component to manage our Sinks.Many.
record Message(String id, String text, String channelId, String timestamp) {}
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
@Component
public class MessagePublisher {
private final Sinks.Many<Message> messageSink =
Sinks.many().multicast().onBackpressureBuffer();
public Flux<Message> getMessageStream() {
return messageSink.asFlux();
}
public void publishMessage(Message message) {
messageSink.tryEmitNext(message);
}
}Main Application & Resolver
Now, we connect our MessagePublisher to the @SubscriptionMapping resolver. The Main class simulates sending a message after a delay.
Run this example to see the server-side publishing in action!
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
import org.springframework.stereotype.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
@SpringBootApplication
public class Main implements CommandLineRunner {
@Autowired
private MessagePublisher messagePublisher;
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
@Override
public void run(String... args) throws Exception {
// Simulate sending a message after 2 seconds
Mono.delay(Duration.ofSeconds(2))
.subscribe(l -> {
Message msg = new Message("2", "Hello from Spring!", "general", "now");
messagePublisher.publishMessage(msg);
System.out.println("Published: " + msg);
});
}
}
@Controller
class MessageSubscriptionController {
private final MessagePublisher messagePublisher;
public MessageSubscriptionController(MessagePublisher messagePublisher) {
this.messagePublisher = messagePublisher;
}
@SubscriptionMapping
public Flux<Message> messageAdded() {
return messagePublisher.getMessageStream();
}
}
record Message(String id, String text, String channelId, String timestamp) {}Filtering Subscription Events
Clients often need updates specific to certain criteria. We can filter the Flux based on arguments passed to the subscription.
Here, clients only receive messages for a specified channelId.
import org.springframework.graphql.data.method.annotation.Argument;
// ... other imports ...
@Controller
class MessageSubscriptionController {
private final MessagePublisher messagePublisher;
public MessageSubscriptionController(MessagePublisher messagePublisher) {
this.messagePublisher = messagePublisher;
}
@SubscriptionMapping
public Flux<Message> messageAdded(@Argument String channelId) {
return messagePublisher.getMessageStream()
.filter(msg -> msg.channelId().equals(channelId));
}
}Decoupling Event Publishing
For better architecture, it's a good practice to decouple event publishing from your core business logic.
- This keeps your services clean and focused.
- It allows multiple independent subscribers to react to the same event without tight coupling.
- Consider using Spring's
ApplicationEventPublisheror a dedicated event bus for this.
Subscription Resolver Check
You've learned how to implement subscription resolvers in Spring Boot. Let's test your understanding!
Recap: Real-time Updates
You've successfully learned how to implement real-time updates using GraphQL subscriptions in Spring Boot!
- Subscription resolvers return a
Flux. Sinks.Manyis used to publish events into theFlux.- You can filter streams based on subscription arguments.
- Decoupling publishing logic improves maintainability.
常见问题解答
「实现实时更新」课时是免费的吗?
是的 — 「实现实时更新」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 GraphQL APIs with Spring Boot 课程的其余内容,请升级到 CoddyKit PRO。 GraphQL APIs with Spring Boot 课程共包含 4 节课。
「实现实时更新」这节课中我会学到什么?
在 Spring Boot 中开发订阅解析器,发布事件并向客户端发送实时数据。 你通过在浏览器中直接运行的动手代码来练习 GraphQL APIs with Spring Boot,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 GraphQL APIs with Spring Boot 需要有经验吗?
无需任何先前经验。CoddyKit 上的 GraphQL APIs with Spring Boot 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「实现实时更新」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 GraphQL APIs with Spring Boot 课中编写并运行代码吗?
能。每节 GraphQL APIs with Spring Boot 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。