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包里的对象

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);
    }
}
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);
    }
}
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{
    @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配置包里相关信息

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;
    }
}
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;
    }
}

事件类型枚举包

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> {

  @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;
    }
}
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);
    }
}
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);
    }
}

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);
    }
}

测试入口

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);
    }
}

项目启动类

@SpringBootApplication
public class EventApplication {

    public static void main(String[] args) {
        SpringApplication.run(EventApplication.class,args);
    }
}

test:

localhost:8080/event/post

resp:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/565257.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

就业班 第三阶段(负载均衡) 2401--4.19 day3 nginx3

二、企业 keepalived 高可用项目实战 1、Keepalived VRRP 介绍 keepalived是什么keepalived是集群管理中保证集群高可用的一个服务软件&#xff0c;用来防止单点故障。 ​ keepalived工作原理keepalived是以VRRP协议为实现基础的&#xff0c;VRRP全称Virtual Router Redundan…

跨语言大模型最新综述

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总…

L2-052 吉利矩阵

题目描述 题解思路 这个道题就是很简单&#xff0c;就跟n皇后问题一样&#xff0c;给矩阵填数&#xff0c;使得矩阵满足一个什么条件&#xff0c;最后求方案数或者方案。很容易想到回溯法&#xff0c;根据数据范围&#xff0c;应该能够确定回溯法是没有问题的。 我们只需要枚举…

《C语言深度解剖》(9):深度剖析数据在内存中的存储

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《C语言深度解剖》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&am…

双线性插值计算手动实现以及原理

双线性插值计算手动实现以及原理 代码原理 代码 先贴代码吧&#xff0c;原理其实也比较简单&#xff0c;看代码基本也就理解了&#xff0c;时间太晚了&#xff0c;原理后续再补吧。 import torch from torch.nn import functional as F import numpy as np from itertools im…

力扣经典150题第三十六题:旋转图像

目录 力扣经典150题第三十六题&#xff1a;旋转图像引言题目详解解题思路代码实现示例演示复杂度分析总结扩展阅读 力扣经典150题第三十六题&#xff1a;旋转图像 引言 本篇博客介绍了力扣经典150题中的第三十六题&#xff1a;旋转图像。题目要求将给定的 n n 二维矩阵顺时针…

玩转 AIGC!使用 SD-WebUI 实现从文本到图像转换

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 基于大家…

“AI 程序员入职系列”第二弹:如何利用通义灵码光速改写项目编程语言?

通义灵码入职阿里云云原生团队后&#xff0c;已经展示过 Ta 生成单元测试和自动生成代码的强大实力。今天&#xff0c;阿里云后端工程师云徊将从项目开发的实际需求出发&#xff0c;演示通义灵码在开发工作中可提供的帮助。 通义灵码在 Git 开发项目中起到了哪些作用&#xff…

小白必备:Python必须掌握的十大模块,建议收藏!

前言 Python 是一种高级、解释型和通用动态编程语言&#xff0c;侧重于代码的可读性。 它在许多组织中使用&#xff0c;因为它支持多种编程范例。 它还执行自动内存管理。 它是世界上最受欢迎的编程语言之一。 这是有很多原因的&#xff1a; 这很容易学习。它超级多才多艺。…

Jenkins 构建踩坑经历

SourceLink.Create.CommandLine.targets(30,5): error : unable to convert OriginUrl: 打开报错“SourceLink.Create.CommandLine.targets”文件所在编辑此文件&#xff0c;找到30行 修改如下

浮动的4个案例详解:网易云音乐列表/导航,淘宝banner区,文字环绕

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

如何在Windows10中管理自动维护?这里有详细步骤

为了使你的Windows操作系统和应用程序保持最新状态并保持良好的健康&#xff0c;有必要定期进行Windows维护。 ​快速的方法是启动自动维护&#xff0c;这样Windows就可以每天自动运行维护任务&#xff0c;包括软件更新、安全扫描和系统诊断。本文介绍了一个在Windows10中管理…

Arthas介绍及使用技巧

文章目录 简介能做什么&#xff1f; 使用下载并启动arthas选择应用 java 进程退出 arthas 常用查看命令帮助查看 dashboard通过 thread 命令来获取到线程的栈通过 jad 来反编译 Classwatch 查看方法出入参、sc 搜索类: 查看已加载类所在的包monitor 方法执行监控trace 方法内调…

HTML 如何实现一个带间隙的圆环

实际效果&#xff1a; ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/5e634cedded9424d96fbe6d46f34f61a.png#pic_center 代码实现&#xff1a; HTML部分&#xff1a; <svg width"500" height"500" viewBox"0 0 100 100">&…

VideoComposer: Compositional Video Synthesis with Motion Controllability

decompose videos into three distinct types of conditions: textual conditions, spatial conditions, temperal conditions 条件的内容&#xff1a; a. textual condition: coarse grained visual content and motions, 使用openclip vit-H/14的text encoder b. spatial co…

Gradle的Tasks显示不全

前言&#xff1a; 在修改了app级别的build.gradle或者project的buid.gradle之后&#xff0c;在androidstudio的gradle中没有看到配置的task。原因是需要打开gradle的配置项 1.修改位置&#xff1a; 2.将划红线的这项配置的对勾去掉&#xff0c;然后点击”大象“同步

世界读书日:探索阅读的多样性样性——漫画、图解、图形化立体图书

在当今信息爆炸的时代&#xff0c;阅读已经不再局限于传统的书籍形式。随着科技的发展和文化的多样化&#xff0c;人们可以通过多种形式来获取知识和享受阅读的乐趣。从漫画、图解到图形化立体图书&#xff0c;每一种形式都有其独特的魅力&#xff0c;适合不同类型的读者和学习…

机器人实验室CNRS-AIST JRL, IRL介绍

一、背景 作为搞机器人方向的学生&#xff0c;必须时常关注国际上顶尖实验室的研究成果&#xff0c;以免自己做的方向out&#xff0c;除了大家耳熟能详的Boston Dynamics&#xff0c;还有许多非常厉害的机器人实验室值得我们关注&#xff0c;如日本的CNRS-AIST JRL, IRL实验室…

认识产品经理

一、合格的产品经理 1、什么是产品 解决某个问题的东西&#xff0c;称为产品 键盘可以打字&#xff0c;想喝水了可以用水壶&#xff0c;在超市想找一款扫把会有导购员服务 产品有颜色、大小等等区别&#xff0c;也有有形和无形的区别 2、什么是产品经理 想清楚怎么设计产品…

c++ - 类与对象 - explicit关键字 | static成员 | 友元 | 内部类 | 匿名对象

文章目录 一、 explicit关键字二、static成员三、友元四、内部类五、匿名对象 一、 explicit关键字 1、隐式类型转换 再进行隐式类型转换是会产生一个临时变量tmp,再用临时变量进行赋值。 如&#xff1a; double d 1.2; //再用 d 给 i 进行赋值时&#xff0c;会进行隐式类型…