Mô hình Publish-Subscribe trong Spring

Mô hình Publish-Subscribe là gì?

Các hệ thống trung gian tin nhắn hiện nay thường áp dụng mô hình này. Trong mô hình này, người phát hành sự kiện (Publisher) và người đăng ký sự kiện (Subscriber) được tách rời thông qua một kênh sự kiện (Event Channel) trung gian. Mặc dù nguyên lý cơ bản tương tự như mẫu quan sát (Observer), nhưng mô hình Publish-Subscribe còn có khái niệm kênh sự kiện, giúp giải quyết sự phụ thuộc giữa chủ đề (Subject) và người quan sát (Observer). Người phát hành và người đăng ký không cần phải biết chi tiết về nhau.

Ứng dụng thực tế trong Spring

Spring hỗ trợ mô hình Publish-Subscribe thông qua cơ chế sự kiện, nhằm tách biệt các thành phần trong ứng dụng. Cơ chế này cho phép các phần khác nhau trong ứng dụng giao tiếp một cách lỏng lẻo, rất phù hợp với các mô hình kinh doanh yêu cầu trạng thái hoặc luồng hoạt động phức tạp.

Các thành phần chính trong mô hình:

  • Sự kiện (Event): Là đại diện cho thay đổi trạng thái hoặc hành động trong ứng dụng. Trong Spring, sự kiện thường là một lớp POJO chứa thông tin liên quan đến sự kiện đó.
  • Người phát hành (Event Publisher): Thành phần chịu trách nhiệm gửi sự kiện. ApplicationContext trong Spring đóng vai trò này, cho phép ứng dụng phát sự kiện và truyền nó tới các listener đã đăng ký.
  • Người đăng ký (Event Subscriber): Thành phần xử lý các sự kiện cụ thể. Trong Spring, người đăng ký thường là lớp triển khai interface ApplicationListener hoặc sử dụng annotation @EventListener.
  • Gửi sự kiện (ApplicationEventPublisher): Quá trình phát sự kiện trong Spring thông qua interface ApplicationEventPublisher, cho phép gửi sự kiện đến các listener đã đăng ký.

Các bước thiết kế

  1. Tạo lớp sự kiện: Định nghĩa các loại sự kiện trong ứng dụng, thường là các lớp POJO đơn giản.
  2. Tạo người phát hành: Thiết lập thành phần phát sự kiện, có thể dùng ApplicationEventPublisher hoặc annotation @EventListener.
  3. Tạo người đăng ký: Xây dựng thành phần lắng nghe và phản hồi sự kiện bằng cách triển khai ApplicationListener hoặc dùng @EventListener.
  4. Kích hoạt sự kiện: Gọi phương thức phát sự kiện tại thời điểm thích hợp, khi đó tất cả các listener sẽ nhận được thông báo và xử lý.

Ví dụ minh họa với trường hợp đăng ký người dùng:

Lớp sự kiện UserRegisteredEvent

import org.springframework.context.ApplicationEvent;

public class UserRegisteredEvent extends ApplicationEvent {
    private final String username;

    public UserRegisteredEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

Dịch vụ UserRegistrationService - phát sự kiện

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class UserRegistrationService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void registerUser(String username) {
        // Logic đăng ký người dùng
        // ...

        // Phát sự kiện sau khi đăng ký thành công
        eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
    }
}

Dịch vụ UserNotificationService - lắng nghe và xử lý sự kiện

@Service
public class UserNotificationService {

    @EventListener
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        // Xử lý khi người dùng đăng ký
        String username = event.getUsername();
        System.out.println("Thông báo gửi cho người dùng: " + username);
    }
}

Lớp khởi chạy ứng dụng Spring

@SpringBootApplication
@RequiredArgsConstructor
public class Application implements CommandLineRunner {

    private final UserRegistrationService userRegistrationService;

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setWebApplicationType(WebApplicationType.NONE);
        app.run(args);
    }

    @Override
    public void run(String... args) {
        // Đăng ký người dùng khi khởi động
        userRegistrationService.registerUser("peng");
    }
}

Kết quả đầu ra của chương trình:

Thông báo gửi cho người dùng: peng

Mã nguồn ví dụ có thể tìm thấy tại repo GitHub với đường dẫn package com.github.meeting.demo.pattern.pubsub;.

Thẻ: Spring event-driven Publish-Subscribe applicationevent @EventListener

Đăng vào ngày 4 tháng 7 lúc 13:35