一:前置知识
Spring Event是Spring框架提供的一种事件机制,用于处理组件之间的通信。在复杂的系统中,模块或组件之间的通信是必不可少的。Spring Event可以用于以下场景:
1.系统间解耦:模块或组件之间通过事件进行通信,而不需要相互依赖。
2.异步处理:通过事件,可以异步处理某些任务,提高系统的处理能力。
3.日志记录:通过监听事件,记录系统的运行情况,如用户登录、数据修改等
注:业务系统一定要先实现优雅关闭服务,才能使用 Spring Event,这个和MQ还是有点区别,Spring Event和 MQ 都属于订阅发布模式的应用,然而 MQ 比 SpringEvent 强大且复杂。MQ 更适合应用之间的解耦、隔离、事件通知。例如订单支付、订单完成、订单履约完成等等事件需要广播出去,通知下游其他微服务, 这种场景更适合使用 MQ 。
然而对于应用内需要订阅发布的场景更适合使用 SpringEvent 。两者并不矛盾,MQ 能力更强大,技术方案也更"重"一些。Spring Event 更加小巧适合应用内订阅发布,实现业务逻辑解耦。
像我之前的公司里。订单服务内部使用的是Spring Event 进行订单内部逻辑的异步和解耦。订单服务和其他服务之间使用的是Kafka来进行的解耦合数据通信。
二:基础封装
代码层级接口见下截图
base包里的对象
java
package com.jinyi.event.base;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.context.ApplicationEvent;
import java.time.Clock;
public class BaseEvent extends ApplicationEvent {
public BaseEvent() {
super("");
}
public BaseEvent(Object source) {
super(source);
}
public BaseEvent(Object source, Clock clock) {
super(source, clock);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
java
package com.jinyi.event.base;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class EventBusCenter {
@Resource
private ApplicationEventPublisher applicationEventPublisher;
public void post(BaseEvent event) {
applicationEventPublisher.publishEvent(event);
}
}
java
package com.jinyi.event.base;
import com.jinyi.event.enums.OrderTrackTypeEnum;
import lombok.Data;
import java.io.Serializable;
@Data
public class OrderStatusChangeDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long userId;
private Long orderNo;
private OrderTrackTypeEnum orderStatusChange;
public OrderStatusChangeDTO() {
}
public OrderStatusChangeDTO(Long orderNo, OrderTrackTypeEnum orderStatusChange) {
this();
this.orderNo = orderNo;
this.orderStatusChange = orderStatusChange;
}
public OrderStatusChangeDTO(Long userId, Long orderNo, OrderTrackTypeEnum orderStatusChange) {
this();
this.orderNo = userId;
this.orderNo = orderNo;
this.orderStatusChange = orderStatusChange;
}
}
业务事件里的对象
java
package com.jinyi.event.bizEvent;
import com.jinyi.event.base.OrderStatusChangeDTO;
import com.jinyi.event.enums.OrderTrackTypeEnum;
/**
* 订单支付事件
* @date 2024/4/19 15:55
* @desc
*/
public class OrderPayEvent extends OrderStatusChangeEvent {
public OrderPayEvent(Object source, OrderStatusChangeDTO dto) {
super(source, dto);
if (null == dto.getOrderStatusChange()) {
dto.setOrderStatusChange(defaultStatus());
}
}
private OrderTrackTypeEnum defaultStatus() {
return OrderTrackTypeEnum.ORDER_PAY;
}
}
java
package com.jinyi.event.bizEvent;
import com.jinyi.event.base.BaseEvent;
import com.jinyi.event.base.OrderStatusChangeDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 订单变化事件BaseEvent对象 :存放该事件通用的param
*
* @date 2024/4/19 15:46
* @desc
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class OrderStatusChangeEvent extends BaseEvent {
private OrderStatusChangeDTO orderStatusChangeDTO;
public OrderStatusChangeEvent(Object source, OrderStatusChangeDTO dto) {
super(source);
this.orderStatusChangeDTO = dto;
}
}
java
package com.jinyi.event.bizEvent;
import com.jinyi.event.base.OrderStatusChangeDTO;
import com.jinyi.event.enums.OrderTrackTypeEnum;
/**
* @date 2024/4/19 16:02
* @desc
*/
public class OrderUserCancelEvent extends OrderStatusChangeEvent {
public OrderUserCancelEvent(Object source, OrderStatusChangeDTO dto) {
super(source, dto);
if (null == dto.getOrderStatusChange()) {
dto.setOrderStatusChange(defaultStatus());
}
}
private OrderTrackTypeEnum defaultStatus() {
return OrderTrackTypeEnum.USER_CANCEL;
}
}
java
package com.jinyi.event.bizEvent;
import com.jinyi.event.base.OrderStatusChangeDTO;
import com.jinyi.event.enums.OrderTrackTypeEnum;
import lombok.Getter;
/**
* 扣减库存事件
* @date 2024/4/19 15:55
* @desc
*/
public class StockReduceEvent extends OrderStatusChangeEvent{
@Getter
private final Long goodsId;
public StockReduceEvent(Object source, OrderStatusChangeDTO dto, Long goodsId) {
super(source, dto);
this.goodsId = goodsId;
if (null == dto.getOrderStatusChange()) {
dto.setOrderStatusChange(defaultStatus());
}
}
private OrderTrackTypeEnum defaultStatus() {
return OrderTrackTypeEnum.STOCK_REDUCE;
}
}
config配置包里相关信息
java
package com.jinyi.event.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.annotation.Resource;
/**
* 实现异步处理事件配置
* @date 2024/4/19 15:31
* @desc
*/
@Configuration
public class EventConfig {
@Resource
@Qualifier("asyncTaskExecutor")
private ThreadPoolTaskExecutor taskExecutor;
/**
* 默认情况下,事件会被同步发送给所有监听器,这意味着如果监听器耗时较长,则会阻塞后续的监听器和发布线程.
* @return
*/
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(taskExecutor);
return multicaster;
}
}
java
package com.jinyi.event.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class ExecutorConfig {
@Value("${executor.corePoolSize:4}")
private Integer corePoolSize;
@Value("${executor.queueCapacity:1000}")
private Integer queueCapacity;
@Value("${executor.maxPoolSize:6}")
private Integer maxPoolSize;
@Value("${executor.keepAliveSeconds:30}")
private Integer keepAliveSeconds;
@Value("${executor.threadNamePrefix:async-executor-}")
private String threadNamePrefix;
/**
* 线程池
* @return
*/
@Bean
public ThreadPoolTaskExecutor asyncTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix(threadNamePrefix);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setMaxPoolSize(maxPoolSize);
//线程大于coreSize,多余线程数超过30s则销毁
executor.setKeepAliveSeconds(keepAliveSeconds);
// 设置拒绝策略,调用当前线程执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
事件类型枚举包
java
package com.jinyi.event.enums;
public enum OrderTrackTypeEnum {
ORDER_CREATE("order_create", "用户下单"),
ORDER_PAY("order_pay", "完成支付"),
ORDER_FINISH("order_finish", "订单完成"),
USER_CANCEL("user_cancel","用户取消订单"),
STOCK_REDUCE("stock_reduce","扣减库存"),
;
private String operatorType;
private String describe;
OrderTrackTypeEnum(String operatorType, String describe) {
this.operatorType = operatorType;
this.describe = describe;
}
public String getOperatorType() {
return operatorType;
}
public String getDescribe() {
return describe;
}
public static String getDescribeByHandle(String handle) {
for (OrderTrackTypeEnum handleEnum : OrderTrackTypeEnum.values()) {
if (handleEnum.getOperatorType().equals(handle)) {
return handleEnum.getDescribe();
}
}
return null;
}
}
handle包
java
package com.jinyi.event.handle;
import com.jinyi.event.base.BaseEvent;
import org.springframework.context.ApplicationListener;
/***
* event handle 基础类
*
**/
public interface IEventHandler<T extends BaseEvent> extends ApplicationListener<T> {
@Override
default void onApplicationEvent(T event) {
try {
if (support(event)) {
handle(event);
}
} catch (Throwable e) {
handleException(e);
}
}
/**
* 事件处理统一方法
* @param event
*/
void handle(T event);
/**
* 处理异常(默认不进行异常处理)
*
* @param exception
*/
default void handleException(Throwable exception) {};
/**
* 子类重写可自定义事件支持是否开启
* @param event
* @return
*/
default boolean support(T event) {
return true;
}
}
java
package com.jinyi.event.handle;
import com.jinyi.event.base.OrderStatusChangeDTO;
import com.jinyi.event.bizEvent.OrderPayEvent;
import com.jinyi.event.bizEvent.OrderStatusChangeEvent;
import com.jinyi.event.bizEvent.OrderUserCancelEvent;
import com.jinyi.event.enums.OrderTrackTypeEnum;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* 订单支付事件
*
* @author huangchong
* @date 2024/4/19 15:55
* @desc
*/
@Component
public class OrderPayEventHandle implements IEventHandler<OrderStatusChangeEvent> {
/**
* 订单支付事件处理
*
* @param event
*/
@Override
public void handle(OrderStatusChangeEvent event) {
if (event instanceof OrderPayEvent){
OrderStatusChangeDTO changeDTO = event.getOrderStatusChangeDTO();
OrderTrackTypeEnum orderStatusChange = changeDTO.getOrderStatusChange();
System.out.println("Thread:" + Thread.currentThread().getName() + orderStatusChange.getDescribe() + "-----");
}else {
System.out.println("Thread:" + Thread.currentThread().getName() + "--OrderPayEventHandle 通用订单状态改变---");
}
}
@Override
public void handleException(Throwable exception) {
IEventHandler.super.handleException(exception);
}
@Override
public boolean support(OrderStatusChangeEvent event) {
return IEventHandler.super.support(event);
}
}
java
package com.jinyi.event.handle;
import com.jinyi.event.base.OrderStatusChangeDTO;
import com.jinyi.event.bizEvent.OrderPayEvent;
import com.jinyi.event.bizEvent.OrderStatusChangeEvent;
import com.jinyi.event.bizEvent.OrderUserCancelEvent;
import com.jinyi.event.enums.OrderTrackTypeEnum;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class OrderUserCancelEventHandle implements IEventHandler<OrderStatusChangeEvent> {
@Override
public void handle(OrderStatusChangeEvent event) {
if (event instanceof OrderUserCancelEvent){
OrderStatusChangeDTO changeDTO = event.getOrderStatusChangeDTO();
OrderTrackTypeEnum orderStatusChange = changeDTO.getOrderStatusChange();
System.out.println("Thread:" + Thread.currentThread().getName() + orderStatusChange.getDescribe() + "-----");
}else {
System.out.println("Thread:" + Thread.currentThread().getName() + "--OrderUserCancelEventHandle 通用订单状态改变---");
}
}
@Override
public boolean support(OrderStatusChangeEvent event) {
return IEventHandler.super.support(event);
}
}
java
package com.jinyi.event.handle;
import com.jinyi.event.base.OrderStatusChangeDTO;
import com.jinyi.event.bizEvent.OrderStatusChangeEvent;
import com.jinyi.event.bizEvent.OrderUserCancelEvent;
import com.jinyi.event.bizEvent.StockReduceEvent;
import com.jinyi.event.enums.OrderTrackTypeEnum;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class StockReduceEventHandle implements IEventHandler<OrderStatusChangeEvent> {
@Override
public void handle(OrderStatusChangeEvent event) {
Boolean flag = event instanceof StockReduceEvent;
if (flag ) {
OrderStatusChangeDTO changeDTO = event.getOrderStatusChangeDTO();
OrderTrackTypeEnum orderStatusChange = changeDTO.getOrderStatusChange();
StockReduceEvent stockReduceEvent = (StockReduceEvent) event;
Long goodsId = stockReduceEvent.getGoodsId();
System.out.println("Thread:" + Thread.currentThread().getName() + orderStatusChange.getDescribe() + "-111----");
}else {
System.out.println("Thread:" + Thread.currentThread().getName() + "--StockReduceEventHandle 通用订单状态改变---");
}
}
@Override
public boolean support(OrderStatusChangeEvent event) {
return IEventHandler.super.support(event);
}
}
测试入口
java
package com.jinyi.event.send;
import com.jinyi.event.base.EventBusCenter;
import com.jinyi.event.base.OrderStatusChangeDTO;
import com.jinyi.event.bizEvent.OrderPayEvent;
import com.jinyi.event.bizEvent.OrderStatusChangeEvent;
import com.jinyi.event.enums.OrderTrackTypeEnum;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author huangchong
* @date 2024/4/19 16:33
* @desc
*/
@RestController
@RequestMapping("/event")
public class BizPost {
@Resource
private EventBusCenter eventBusCenter;
@GetMapping("/post")
public void bizPost() {
OrderStatusChangeDTO changeDTO =new OrderStatusChangeDTO();
changeDTO.setUserId(1L);
changeDTO.setOrderNo(100L);
changeDTO.setOrderStatusChange(OrderTrackTypeEnum.ORDER_PAY);
//仅触发订单支付 和 其他 通用handle
OrderStatusChangeEvent event = new OrderPayEvent(this, changeDTO);
eventBusCenter.post(event);
//广播订单状态变更时间
// OrderStatusChangeEvent event1 = new OrderStatusChangeEvent(this, changeDTO);
// eventBusCenter.post(event1);
}
}
项目启动类
java
@SpringBootApplication
public class EventApplication {
public static void main(String[] args) {
SpringApplication.run(EventApplication.class,args);
}
}
test:
java
localhost:8080/event/post
resp: