Spring 中的釋出-訂閱模式

怦怦On發表於2024-04-18

釋出訂閱模式是怎樣的?

現在市面上流行的很多訊息中介軟體就是採用的該種模式,這種模式 在實際業務中 將 事件釋出者(Publisher) 與 事件訂閱者 (Subscriber)透過額外的事件通道(Event Channel)來解耦,其基本原理與先前提到的觀察者模式有些許類似,但釋出訂閱模式額外存在了Event Channel 的概念,也因此解決了 主題(Subject)和 Observer(觀察者)之間的耦合,釋出者和訂閱者就互動細節上不再需要相互關聯,下圖可以看到兩種模式之間的區別。

在 Spring 中的實際應用

在Spring框架中,釋出-訂閱模式是透過事件驅動的方式實現的,用於解耦應用程式中的元件。Spring的事件機制允許應用程式中的各個部分以鬆散耦合的方式進行通訊,這種方式在日常編碼中最適合應對一些需要狀態機的業務模型, 下面就來講下 在 Spring 中的釋出訂閱涉及到的概念

  • 事件(Event):事件是應用程式中的狀態變化或動作,對應於某個特定的行為或狀態。在Spring中,事件通常是一個POJO(Plain Old Java Object)類,它包含了與該事件相關的資訊。
  • 事件釋出者(Event Publisher):事件釋出者是負責釋出事件的元件。在Spring中,ApplicationContext(應用程式上下文)充當了事件釋出者的角色。透過ApplicationContext,應用程式可以釋出事件,並將其傳遞給已註冊的事件監聽器。
  • 事件訂閱者(Event Subscriber):事件訂閱者是負責處理特定型別事件的元件。在Spring中,事件訂閱者通常是實現了ApplicationListener介面的類,用於監聽並處理髮布的事件。
  • 事件推送(ApplicationEventPublisher):事件推送是指透過ApplicationEventPublisher介面在Spring中釋出事件的過程。這個介面可以在ApplicationContext中使用,用於向已註冊的監聽器廣播事件的發生。

設計步驟

  1. 定義事件類:首先,定義應用中的各種事件,這些事件可以是任何與應用狀態變化或行為相關的事情。事件通常是簡單的POJO類。
  2. 定義釋出者:建立事件釋出者(Publisher)元件,它負責釋出事件。在Spring中,你可以使用ApplicationEventPublisher介面或者直接在Bean方法上標註@EventListener註解來實現。
  3. 定義訂閱者:建立事件訂閱者(Subscriber)元件,它負責訂閱感興趣的事件並對事件做出響應。在Spring中,你可以定義實現ApplicationListener介面的類來作為事件監聽器,或者使用@EventListener註解在方法上訂閱事件。
  4. 觸發事件:在適當的時候,透過釋出者釋出事件。一旦事件被髮布,所有已經註冊的訂閱者將會收到事件通知並執行相應的邏輯。

這裡會以簡單的 使用者註冊的場景來介紹下 Spring ApplicationEventPublisher 的使用方式

定義事件類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;
    }
}



建立UserRegistrationService服務,負責註冊新使用者併發布事件

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) {
        // 註冊新使用者邏輯
        // ...

        // 釋出使用者註冊事件,這裡使用 eventPublisher 將 預先定義好的事件交由 Spring 的Event Channel 管理
        eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
    }
}

建立UserNotificationService服務,訂閱UserRegisteredEvent事件併傳送通知
@Service
public class UserNotificationService {

    @EventListener
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        // 處理使用者註冊事件,例如傳送通知
        String username = event.getUsername();
        System.out.println("Notification sent for user: " + username);
    }
}

建立 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) {
        // 在啟動時註冊新使用者
        userRegistrationService.registerUser("peng");
    }
}

最後程式的輸出結果 如下所示:

Notification sent for user: peng

案例中的程式碼已釋出在 github倉庫 , 路徑為package com.github.meeting.demo.pattern.pubsub;.

相關文章