SpringBoot Event,事件驅動輕鬆實現業務解耦
1 什麼是事件驅動
Spring 官方文檔
- https://spring.io/event-driven/
AWS Event Driven
- https://aws.amazon.com/cn/what-is/eda/
簡單來說事件驅動是一種行爲型設計模式,通過建立一對多的依賴關係,使得當一個對象的狀態發生變化時,所有依賴它的對象都能自動接收通知並更新。即將自身耦合的行爲進行拆分,使拆分出的行爲根據特定的狀態變化(觸發條件)自動觸發。
2 事件驅動核心組件
-
被觀察者(Subject):負責維護觀察者列表,並在狀態變化時通知觀察者。被觀察者可以是一個類或對象。
-
觀察者(Observer):定義一個更新接口,使得在狀態變化時能夠接收被觀察者的通知。觀察者對象需要註冊到被觀察者上,以便接收通知。
-
通知(Notify):被觀察者在狀態變化時會調用觀察者的更新方法,通知它們有關狀態的變化。
-
訂閱(Subscribe)和取消訂閱(Unsubscribe):觀察者可以通過訂閱和取消訂閱操作來註冊和註銷對被觀察者的關注。
3 實現方式
-
Guava
-
Spring Event
版本依賴
-
JDK 17
-
Spring Boot 3.2.0
-
Guava 33.0.0-jre
Tips
在僅將事件拆分出事件對象與事件監聽對象後,通過事件總線推送事件,仍是單線程執行,在 SpringBoot 中需要兩個註解來實現異步執行。
-
@EnableAsync 類註解,在配置類上標記開啓 SpringBoot 異步線程
-
@Async 方法註解,表示註解的方法異步執行。@Async 註解的方法僅在通過容器中獲取的對象調用方法爲異步執行,非容器對象方法調用無效
導入依賴
<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