SpringBoot Event,事件驅動輕鬆實現業務解耦

1 什麼是事件驅動

Spring 官方文檔

AWS Event Driven

簡單來說事件驅動是一種行爲型設計模式,通過建立一對多的依賴關係,使得當一個對象的狀態發生變化時,所有依賴它的對象都能自動接收通知並更新。即將自身耦合的行爲進行拆分,使拆分出的行爲根據特定的狀態變化(觸發條件)自動觸發。

2 事件驅動核心組件

3 實現方式

版本依賴

Tips

在僅將事件拆分出事件對象與事件監聽對象後,通過事件總線推送事件,仍是單線程執行,在 SpringBoot 中需要兩個註解來實現異步執行。

導入依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>33.0.0-jre</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

4 基於 Guava 實現

創建事件對象

import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

/**
 * Guava 實現事件對象
 */
@Data
public class GuavaEvent implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    private String data;

    public GuavaEvent(String data) {
        this.data = data;
    }
}

創建事件監聽

import com.google.common.eventbus.Subscribe;
import com.yiyan.study.guava.event.GuavaEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * Guava 事件監聽
 */
@Component
@Slf4j
public class GuavaEventListener {

    @Subscribe
    @Async
    public void onEvent(GuavaEvent event) throws InterruptedException {
        // 模擬業務耗時
        Thread.sleep(500);
        log.info("Guava - GuavaEvent onEvent : {}", event.getData());
    }
}

創建事件總線,並註冊事件監聽

import com.google.common.eventbus.EventBus;
import com.yiyan.study.guava.listener.GuavaEventListener;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Guava 事件總線
 */
@Configuration
public class GuavaEventBus {

    @Resource
    private GuavaEventListener guavaEventListener;

    @Bean
    public EventBus initialize() {
        EventBus eventBus = new EventBus();
        // 註冊監聽
        eventBus.register(guavaEventListener);
        return eventBus;
    }
}

編寫 Controller 測試接口

import com.google.common.eventbus.EventBus;
import com.yiyan.study.guava.event.GuavaEvent;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 事件測試Controller
 */
@Slf4j
@RestController
public class EventController {

    @Resource
    private EventBus eventBus;

    @GetMapping("/event/guava")
    public void guavaPost(@RequestParam(value = "message") String message) {
        StopWatch stopWatch = new StopWatch("guava-event");
        stopWatch.start();
        eventBus.post(new GuavaEvent(message));
        stopWatch.stop();
        log.info("guava-event Request Log: \r{}", stopWatch.prettyPrint());
    }
}

5 基於 Spring Event 實現

Spring Boot 自己維護了一個 ApplicationEventPublisher 的事件註冊 Bean,通過 @EventListener 註解標識監聽事件。無需自己在註冊一個消息總線。

創建事件對象

import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

/**
 * Spring 事件對象
 */
@Data
public class SpringEvent implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    private String data;

    public SpringEvent(String data) {
        this.data = data;
    }
}

註冊監聽對象

import com.yiyan.study.spring.event.SpringEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * Spring event listener
 */
@Component
@Slf4j
public class SpringEventListener {
    
    @EventListener
    @Async
    public void onApplicationEvent(SpringEvent event) throws InterruptedException {
        // 模擬業務耗時
        Thread.sleep(500);
        log.info("SpringEvent received: {}", event.getData());
    }
}

補充測試接口

package com.yiyan.study.controller;

import com.google.common.eventbus.EventBus;
import com.yiyan.study.guava.event.GuavaEvent;
import com.yiyan.study.spring.event.SpringEvent;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 事件測試Controller
 */
@Slf4j
@RestController
public class EventController {

    @Resource
    private EventBus eventBus;
    @Resource
    private ApplicationEventPublisher eventPublisher;

    @GetMapping("/event/guava")
    public void guavaPost(@RequestParam(value = "message") String message) {
        StopWatch stopWatch = new StopWatch("guava-event");
        stopWatch.start();
        eventBus.post(new GuavaEvent(message));
        stopWatch.stop();
        log.info("guava-event Request Log: \r{}", stopWatch.prettyPrint());
    }

    @GetMapping("/event/spring")
    public void springPublish(@RequestParam(value = "message") String message) {
        StopWatch stopWatch = new StopWatch("spring-event");
        stopWatch.start();
        eventPublisher.publishEvent(new SpringEvent(message));
        stopWatch.stop();
        log.info("spring-event Request end: \r{}", stopWatch.prettyPrint());
    }
}

6 測試

來源:https://blog.csdn.net/m0_55712478/article/details/135211874

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/FQ4LMjlZyIDsRvL260YuRg