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:

相关推荐
乌啼霜满天249几秒前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn6 分钟前
java入门 自定义springboot starter
java·开发语言·spring boot
Grey_fantasy17 分钟前
高级编程之结构化代码
java·spring boot·spring cloud
新知图书18 分钟前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
盛夏绽放39 分钟前
Node.js 和 Socket.IO 实现实时通信
前端·后端·websocket·node.js
Ares-Wang1 小时前
Asp.net Core Hosted Service(托管服务) Timer (定时任务)
后端·asp.net
苹果酱05671 小时前
前端面试vue篇:Vue2 和 Vue3 在设计和性能上有显著区别
java·spring boot·毕业设计·layui·课程设计
刘大浪2 小时前
后端数据增删改查基于Springboot+mybatis mysql 时间根据当时时间自动填充,数据库连接查询不一致,mysql数据库连接不好用
数据库·spring boot·mybatis
一只爱撸猫的程序猿2 小时前
简单实现一个系统升级过程中的数据平滑迁移的场景实例
数据库·spring boot·程序员
Rverdoser2 小时前
RabbitMQ的基本概念和入门
开发语言·后端·ruby