1.基于Springboot对SpringEvent初步封装

一:前置知识

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:

相关推荐
初晴~1 小时前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱581361 小时前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳1 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾1 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
黑胡子大叔的小屋2 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
计算机毕设孵化场2 小时前
计算机毕设-基于springboot的校园社交平台的设计与实现(附源码+lw+ppt+开题报告)
spring boot·课程设计·计算机毕设论文·计算机毕设ppt·计算机毕业设计选题推荐·计算机选题推荐·校园社交平台
苹果醋33 小时前
Golang的文件加密工具
运维·vue.js·spring boot·nginx·课程设计
小林coding3 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者3 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu