首页 > 其他分享 >在Spring中实现事件发布与监听:实用指南

在Spring中实现事件发布与监听:实用指南

时间:2024-11-01 16:44:32浏览次数:4  
标签:指南 Spring 开票 接口 事件 监听器 机制 监听

Spring框架事件机制的背景和重要性

背景
  1. 解耦设计

    • 在复杂的应用程序中,组件之间的紧密耦合会导致代码难以维护和扩展。事件机制提供了一种解耦的方式,允许组件通过事件进行通信,而无需直接依赖。
  2. 异步处理

    • 事件机制支持异步处理,可以在不阻塞主线程的情况下处理耗时操作,提高应用的响应速度和用户体验。
  3. 基于发布-订阅模式

    • Spring的事件机制采用了发布-订阅模式,使得事件的发布者和订阅者可以独立演化。发布者无需知道谁在监听这些事件,而监听者也不需要知道事件的来源。
重要性
  1. 增强可维护性

    • 通过事件机制,系统的各个部分可以独立发展和演化,减少了模块之间的依赖性,提高了代码的可维护性。
  2. 支持事件驱动架构

    • 在微服务架构和现代应用程序中,事件驱动模型越来越受到重视。Spring的事件机制使得构建事件驱动的系统变得简单和高效。
  3. 提高系统灵活性

    • 开发者可以轻松添加新的事件监听器,而不影响现有的代码逻辑,从而提高了系统的灵活性和扩展性。
  4. 简化异步操作

    • Spring的事件机制支持异步事件处理,简化了多线程编程的复杂性,使得开发者可以更专注于业务逻辑的实现。
  5. 丰富的生态系统

    • Spring框架与Spring Boot等项目的结合,为事件机制提供了丰富的配置选项和易用的工具,使得开发者可以快速上手和应用。

Spring事件机制概述 

1. 什么是事件机制?

事件机制是一种用于组件之间通信的模式,它允许系统中的不同部分通过事件进行交互,而不需要直接调用彼此的接口。这种机制在许多现代软件架构中被广泛使用,特别是在解耦和异步处理的场景中。

2. Spring事件机制的基本概念

  • 事件(Event):表示发生的某个动作或状态变化,通常是一个POJO(普通Java对象),可以包含事件相关的数据。

  • 事件发布者(Publisher):负责发布事件的组件。在Spring中,任何Spring管理的bean都可以充当事件发布者。

  • 事件监听器(Listener):负责响应特定事件的组件。当某个事件被发布时,所有注册到该事件的监听器都会被调用。

3. 事件的工作流程

  1. 发布事件:事件发布者通过ApplicationEventPublisher接口发布事件。

  2. 监听事件:事件监听器通过实现ApplicationListener接口或使用@EventListener注解来注册自己,以便接收特定的事件。

  3. 处理事件:当事件被发布后,Spring框架会自动调用所有注册的监听器,以处理该事件。

4. Spring事件机制的优点

  • 解耦合:发布者和监听者之间没有直接的依赖关系,使得系统更易于维护和扩展。

  • 灵活性:可以动态添加或移除事件监听器,无需修改事件发布者的代码。

  • 异步处理:支持异步事件处理,提高系统的响应能力和性能。

5. 应用场景

  • 状态变化通知:当某个业务状态发生变化时,通知相关组件进行相应的处理。

  • 系统集成:在微服务架构中,不同服务之间可以通过事件进行异步通信。

  • 复杂业务流程:在复杂的业务流程中,事件机制可以帮助管理各个步骤之间的调用和反馈。

通过掌握Spring的事件机制,开发者能够更有效地构建灵活和可扩展的应用程序,从而提升开发效率和系统的可维护性。

个人理解总结:

我们为什么要用事件发布机制?

举一个实际例子,我们正在开发一个商城业务系统,有订单模块,有余额支付模块,我们查完订单的商品明细,每一种商品对应自己的税收分类编码,开发票时需要根据税收分类编码来设置税率,有各种税率,包括0税率,免税。由于公司业务体量不大,开票接口无法直接对税局,只能对三方,因此我们开票比较谨慎,需要管理员审核后才开。

那么大致的逻辑就是,我们的接口入参是一个订单编码

查看订单编号对应的商品明细,遍历商品明细,查找每个商品的税收分类编号,设置税率

将上述组装好的数据加入发票表中,并为该条数据设置一个唯一的请求ID(幂等性考虑,防止同一条数据重复申请开票,造成税务风险),此时属于待审核状态

管理员在后台审核通过,我们将数据组装好,使用RestTemplate模板来发送请求,开票

上述操作,涉及到新增接口,查询主表接口,查询商品明细接口,查询税收分类编码接口,修改发票主表接口,请求第三方接口,发送邮件接口,发送短信通知接口。

突然有一天接到新的需求,项目马上演示,开票不需要审核,直接开票,并且立刻就要改完这个功能,那么这样的话前端申请开票,后端需要直接开票,不采用任何设计模式的情况下,大概是要写一个耦合性非常高的service,代码异常之乱,并且如果需要重新改成审核开票,可能会出非常多的bug,处在这样的情况下,事件发布机制就能无缝衔接以上这些操作,并且松耦合,符合设计原则:对修改关闭,对扩展开放。

不使用任何框架的情况下,我们怎么实现一个监听器模式呢?

1,定义一个事件类,内容和实体类一样,只需要各种属性,一个有参构造器,调用Event类的构造器,作为被发布的事件源,事件类的作用是承担接口入参的数据源。

2,定义一个监听器接口,提供一个方法,入参就是事件类,主要是给实现层来处理自己的逻辑

2,定义一个发布类,这个类最大的特点是持有一个List<监听器接口>列表,提供两个方法,先注册,后发布,这个类提供两个方法,注册和发布方法。注册的目的是初始化监听器实例,发布方法主要是循环执行List<监听器>,将监听器的监听方法遍历执行。这也就是为什么,发布类执行完,监听器里面就执行。一点都不神奇!

Spring中的实际使用:

1,按照上述思路,先定义事件类

@Getter
@Setter
public class ExpressCallBackEvent extends ApplicationEvent {
    private ExpressNotifyDTO dto;
    public ExpressCallBackEvent(ExpressNotifyDTO dto) {
        super(dto);
        this.dto = dto;
    }
}

2,然后定义监听器,监听器就只需要让实现层自己处理逻辑

@Component
public class ExpressCallBackListener {

    @Autowired
    private IExpressOrderService expressOrderService;//依赖注入实现层

    @EventListener
    public void expressCallBack(ExpressCallBackEvent event) {
        expressOrderService.updateExpressOrderByWx(event.getDto());//实现层自己的方法
    }
}

3,发布类发布事件,这里直接用spring上下文来发布

ExpressNotifyDTO notifyDTO = new ExpressNotifyDTO();
notifyDTO.setCustomOrderNo(dto.getUserWalletPay().getRelatedOrderId()+"");
notifyDTO.setPayment_number("userWallet_"+dto.getUserWalletPay().getAmount());
notifyDTO.setTotal(dto.getUserWalletPay().getAmount().multiply(new BigDecimal("100")).intValue());
//applicationContext是实现层依赖注入进来的,作为发布类
applicationContext.publishEvent(new ExpressCallBackEvent(notifyDTO));

好了,简简单单理解并实现事件发布订阅。

上述实际案例中,最后是通过Aspect环绕通知来解决各个service逻辑组装的,简单贴一下代码算了,环绕通知本篇不做解释,这样就相当快速完成了各个逻辑的合并,既便要改回原来的,只要修改Aspect就可以了,各种操作切换自如。

//演示使用
@Aspect
@Component
public class AutoInvoiceAspect {

    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("execution(* com.smt.web.service.*.addInvoiceAndManyProducts(..))")
    public void autoInvoicePointcut() {}


    @Pointcut("execution(* com.smt.web.service.*.addFarmerInvoice(..))")
    public void autoInputInvoicePointcut() {}
    
    /**
     * 普通开票的切面方法
     * @param joinPoint
     * @return
     * @throws Throwable
     */
     //autoInvoicePointcut()是切点表达式的写法,写法多种多样
     //ProceedingJoinPoint参数也可以换成JoinPoint,有兴趣自行研究这两者的区别(提示:取决于接口是否有返回值)
    @Around("autoInvoicePointcut()")
    public Object invoice(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        applicationContext.publishEvent(new InvoiceEvent(1,(Long) result));
        return result;
    }

    /**
     * 进项发票切面方法
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("autoInputInvoicePointcut()")
    public Object inputInvoice(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        applicationContext.publishEvent(new InvoiceEvent(2,(Long) result));
        return result;
    }

标签:指南,Spring,开票,接口,事件,监听器,机制,监听
From: https://blog.csdn.net/Ta20220617/article/details/143433779

相关文章

  • 【SpringSecurity-2】Springboot + SpringSecurity + Oauth2授权码模式
    目录1、项目验证(1)获取授权码(2)获取access_token2、添加资源服务器(1)创建项目(2)添加依赖(3)创建启动类(4)配置资源服务器(5)创建Rest接口(6)添加application.yml3、获取token,访问资源服务器(1)授权服务器的修改(2)资源服务器token校验方式本篇接上篇内容,拿到授权码后,客户端往......
  • SpringBoot中怎么定义一个stater?自动装配规则到底是怎么的?
    问题引出最近实习发现公司项目中有spring.factories文件,是用来定义starter的,让这个模块可以被其他模块引入pom依赖后直接使用,那为什么还必须用spring.factories配置才能被其他模块使用,直接引入依赖不能直接使用吗?答案是能用,但是只能使用这个模块中的非Bean,也就是非spring容器......
  • spring-boot-configuration-processor无法生效
    引入了依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></dependency>编译项目之后并没有生成target/classes/META-INF/spring-configuration-metadata.json看......
  • 【附源码+论文+ppt+部署教程】基于SpringBoot的网上订餐系统
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,源码已上传。点击文末名片获取个性化定制服务  摘 要随着我国经济的飞速发展,人们的生活速度明显加快,在餐厅吃饭排队的情况到处可见,近年来由于新兴IT行业的空前发展,它与传统餐饮行业也进行了新旧的结合,......
  • SpringBoot 快速实现 api 加密!so easy~
    SpringBoot快速实现api加密!soeasy~项目介绍什么是RSA加密举个栗子第一个场景第二个场景加密实战实战准备新建一个springboot项目引入maven依来启动类Application中添加@EnableSecurity注解在application.yml或者application.properties中添加RSA公钥及私钥对Control......
  • Java - 手写识别; 如何用spring ai和大模型做手写识别教程
    识别后的文字利用大模型提升Java手写识别:更简单、更高效在Java场景中,我们经常需要处理手写识别的问题。过去,这类需求主要依赖于OCR技术,但其效果并不总是稳定。随着大模型的发展,使用大模型进行java手写识别成为了一种更优的选择。通过引入先进的大模型,不仅提高了识别的准......
  • 洛谷题单指南-字符串-P3369 【模板】普通平衡树
    原题链接:https://www.luogu.com.cn/problem/P3369题意解读:平衡树的基本操作,模版题。解题思路:1、二叉搜索树-BST二叉搜索树满足这样的性质:每一个节点的权值大于它的左儿子,小于它的右儿子。对BST进行中序遍历,将得到一个从小到大的有序序列,因此BST是为了维护一个有序序列的动态......
  • Spring Boot 和 Spring Cloud 的区别和联系_1
    ###SpringBoot和SpringCloud的区别和联系在现代软件开发领域,SpringBoot和SpringCloud是两个极其重要的框架,它们在微服务架构中扮演着关键角色。直接回答这个问题,SpringBoot和SpringCloud的主要区别在于:SpringBoot旨在简化Spring应用的创建和开发过程、SpringClou......
  • SpringBoot:Failed to obtain JDBC Connection解决方案
    在第一次给自己的SpringBoot链接mysql的时候会出现很多问题可能的问题和解决方案本地的mysql没办法用root登陆这个一般是因为mysql在初始的时候默认root角色不能用密码登陆的原因可以用sudomysql先用最高权限进入mysql然后查看一下root的信息SELECTUser,Host,pl......
  • 从0搭建 Spring Cloud Alibaba 基础工程框架搭建
    整个项目结构:技术栈:springcloudalibaba、MySQL8、Mybatis-Plus、Nacos、knife4j接口文档、Lombok一.开发环境安装JDK17安装MySQL安装二.工程搭建2.1构建父子工程2.1.1创建父工程创建⼀个空的Maven项目,删除所有代码,只保留pom.xml目录结构:图二  ......