0Pricing
GraphQL APIs with Spring Boot · 课时

实现实时更新

在 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 ApplicationEventPublisher or 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.Many is used to publish events into the Flux.
  • 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 理解 GraphQL 订阅
  2. 实现实时更新
  3. 将 WebSockets 集成到 Spring
  4. 订阅的过滤与扩展
← 返回 GraphQL APIs with Spring Boot