Cơ chế xử lý sự kiện với Spring Event Listener

Cơ chế Event trong Spring Framework là một triển khai của Design Pattern "Observer". Nó giúp giảm sự phụ thuộc (decoupling) giữa các thành phần trong hệ thống bằng cách cho phép các module giao tiếp với nhau thông qua các sự kiện thay vì gọi trực tiếp phương thức của nhau.

1. Các thành phần cốt lõi của Spring Event

Hệ thống sự kiện trong Spring xoay quanh ba thành phần chính:

  • Sự kiện (ApplicationEvent): Đóng vai trò là đối tượng chứa dữ liệu hoặc thông tin về sự thay đổi trạng thái.
  • Người lắng nghe (ApplicationListener): Thành phần nhận và xử lý sự kiện khi nó được phát ra.
  • Người phát sự kiện (ApplicationEventPublisher): Thành phần chịu trách nhiệm kích hoạt và gửi sự kiện đi.

2. Triển khai thực tế

Định nghĩa Sự kiện (Event Source)

Để tạo một sự kiện tùy chỉnh, chúng ta kế thừa lớp ApplicationEvent. Dưới đây là ví dụ về sự kiện cập nhật phiên bản dữ liệu:


import org.springframework.context.ApplicationEvent;

public class DataVersionChangeEvent extends ApplicationEvent {
    private final Long dataId;
    private final int version;

    public DataVersionChangeEvent(Object source, Long dataId, int version) {
        super(source);
        this.dataId = dataId;
        this.version = version;
    }

    public Long getDataId() { return dataId; }
    public int getVersion() { return version; }
}

Tạo Listener (Người lắng nghe)

Spring cung cấp hai cách để tạo Listener: thông qua Interface hoặc sử dụng Annotation.

Cách 1: Sử dụng ApplicationListener Interface


import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class SyncMetadataListener implements ApplicationListener<DataVersionChangeEvent> {
    @Override
    public void onApplicationEvent(DataVersionChangeEvent event) {
        System.out.println("Bắt đầu đồng bộ Metadata cho ID: " + event.getDataId());
        // Logic xử lý tại đây
    }
}

Cách 2: Sử dụng Annotation @EventListener

Cách này linh hoạt hơn vì cho phép một class xử lý nhiều loại sự kiện khác nhau.


import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class AuditLogHandler {

    @EventListener
    public void handleVersionUpdate(DataVersionChangeEvent event) {
        System.out.println("Ghi nhận log cập nhật phiên bản: " + event.getVersion());
    }

    @EventListener
    public void notifyUsers(DataVersionChangeEvent event) {
        System.out.println("Gửi thông báo cho người dùng về dữ liệu " + event.getDataId());
    }
}

Phát sự kiện (Event Publisher)

Để phát đi một sự kiện, chúng ta sử dụng interface ApplicationEventPublisher.


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DataController {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @GetMapping("/update-data")
    public String triggerUpdate() {
        // Thực hiện logic cập nhật dữ liệu...
        DataVersionChangeEvent event = new DataVersionChangeEvent(this, 100L, 5);
        
        // Phát sự kiện
        eventPublisher.publishEvent(event);
        
        return "Event published!";
    }
}

3. Xử lý sự kiện bất đồng bộ (Asynchronous Events)

Mặc định, Spring xử lý các sự kiện một cách đồng bộ (Synchronous). Nghĩa là Publisher sẽ đợi cho đến khi tất cả các Listener xử lý xong mới tiếp tục thực hiện lệnh tiếp theo. Để xử lý bất đồng bộ, chúng ta có hai phương pháp phổ biến:

Phương pháp 1: Sử dụng @Async

Kích hoạt tính năng Async trong ứng dụng bằng @EnableAsync và đánh dấu các phương thức Listener bằng @Async.


@Configuration
@EnableAsync
public class AsyncConfig {
    // Cấu hình Executor nếu cần thiết
}

@Component
public class AsyncNotificationListener {

    @Async
    @EventListener
    public void processLargeTask(DataVersionChangeEvent event) {
        // Task này sẽ chạy trên một thread riêng biệt
        System.out.println("Đang xử lý tác vụ nặng bất đồng bộ...");
    }
}

Phương pháp 2: Cấu hình ApplicationEventMulticaster

Bằng cách định nghĩa lại Bean applicationEventMulticaster, tất cả các sự kiện trong hệ thống sẽ được chuyển sang chế độ bất đồng bộ.


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;

@Configuration
public class EventConfig {

    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return multicaster;
    }
}

4. Thứ tự thực thi của các Listener

Nếu có nhiều Listener cùng lắng nghe một sự kiện và bạn muốn quy định thứ tự xử lý, hãy sử dụng annotation @Order.


@EventListener
@Order(1)
public void firstHandler(DataVersionChangeEvent event) {
    // Chạy đầu tiên
}

@EventListener
@Order(2)
public void secondHandler(DataVersionChangeEvent event) {
    // Chạy sau
}

5. Tổng kết các thành phần hệ thống

Dưới đây là tóm tắt vai trò của các Interface quan trọng trong cơ chế này:

  • ApplicationEvent: Lớp cơ sở cho mọi sự kiện.
  • ApplicationListener: Interface để triển khai logic nhận sự kiện.
  • ApplicationEventPublisher: Cổng để phát sự kiện vào Context.
  • ApplicationEventMulticaster: Bộ điều phối trung tâm, quản lý việc gửi sự kiện đến các Listener phù hợp và quản lý Executor (nếu có).

Thẻ: spring-framework spring-boot Java event-driven observer-pattern

Đăng vào ngày 31 tháng 10 lúc 06:46