一:前置知识
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包里的对象
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);}@Overridepublic String toString() {return ToStringBuilder.reflectionToString(this);}
}
package com.jinyi.event.base;import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;import javax.annotation.Resource;@Component
public class EventBusCenter {@Resourceprivate ApplicationEventPublisher applicationEventPublisher;public void post(BaseEvent event) {applicationEventPublisher.publishEvent(event);}
}
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;}
}
业务事件里的对象
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;}
}
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;}
}
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;}
}
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{@Getterprivate 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配置包里相关信息
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*/@Beanpublic SimpleApplicationEventMulticaster applicationEventMulticaster() {SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();multicaster.setTaskExecutor(taskExecutor);return multicaster;}
}
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*/@Beanpublic 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;}
}
事件类型枚举包
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包
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> {@Overridedefault 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;}
}
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*/@Overridepublic 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 通用订单状态改变---");}}@Overridepublic void handleException(Throwable exception) {IEventHandler.super.handleException(exception);}@Overridepublic boolean support(OrderStatusChangeEvent event) {return IEventHandler.super.support(event);}
}
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> {@Overridepublic 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 通用订单状态改变---");}}@Overridepublic boolean support(OrderStatusChangeEvent event) {return IEventHandler.super.support(event);}
}
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> {@Overridepublic 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 通用订单状态改变---");}}@Overridepublic boolean support(OrderStatusChangeEvent event) {return IEventHandler.super.support(event);}
}
测试入口
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 {@Resourceprivate EventBusCenter eventBusCenter;@GetMapping("/post")public void bizPost() {OrderStatusChangeDTO changeDTO =new OrderStatusChangeDTO();changeDTO.setUserId(1L);changeDTO.setOrderNo(100L);changeDTO.setOrderStatusChange(OrderTrackTypeEnum.ORDER_PAY);//仅触发订单支付 和 其他 通用handleOrderStatusChangeEvent event = new OrderPayEvent(this, changeDTO);eventBusCenter.post(event);//广播订单状态变更时间
// OrderStatusChangeEvent event1 = new OrderStatusChangeEvent(this, changeDTO);
// eventBusCenter.post(event1);}
}
项目启动类
@SpringBootApplication
public class EventApplication {public static void main(String[] args) {SpringApplication.run(EventApplication.class,args);}
}
test:
localhost:8080/event/post
resp: