Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!

这篇具有很好参考价值的文章主要介绍了Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

来源:blog.csdn.net/weixin_42653522/article/details/117151913

1、前言

ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果将实现了 ApplicationListener 接口的 bean 部署到容器中,则每次将 ApplicationEvent 发布到ApplicationContext 时,都会通知到该 bean,这简直是典型的观察者模式。设计的初衷就是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。

Spring 中提供了以下的事件

Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!

2、ApplicationEvent 与 ApplicationListener 应用

推荐一个开源免费的 Spring Boot 实战项目:

https://github.com/javastacks/spring-boot-best-practice

实现

1、自定义事件类,基于 ApplicationEvent 实现扩展

public class DemoEvent extends ApplicationEvent {
    private static final long serialVersionUID = -2753705718295396328L;
    private String msg;

    public DemoEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

2、定义 Listener 类,实现 ApplicationListener接口,并且注入到 IOC 中。等发布者发布事件时,都会通知到这个bean,从而达到监听的效果。

@Component
public class DemoListener implements ApplicationListener<DemoEvent> {
    @Override
    public void onApplicationEvent(DemoEvent demoEvent) {
        String msg = demoEvent.getMsg();
        System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
    }
}

3、要发布上述自定义的 event,需要调用 ApplicationEventPublisher 的 publishEvent 方法,我们可以定义一个实现 ApplicationEventPublisherAware 的类,并注入 IOC来进行调用。

@Component
public class DemoPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void sendMsg(String msg) {
        applicationEventPublisher.publishEvent(new DemoEvent(this, msg));
    }
}

4、客户端调用 publisher

@RestController
@RequestMapping("/event")
public class DemoClient implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/publish")
    public void publish(){
        DemoPublisher bean = applicationContext.getBean(DemoPublisher.class);
        bean.sendMsg("发布者发送消息......");
    }
}

输出结果:

bean-listener 收到了 publisher 发布的消息: 发布者发送消息......

基于注解

我们可以不用实现 AppplicationListener 接口 ,在方法上使用 @EventListener 注册事件。如果你的方法应该侦听多个事件,并不使用任何参数来定义,可以在 @EventListener 注解上指定多个事件。

重写 DemoListener 类如下:

public class DemoListener {
    @EventListener(value = {DemoEvent.class, TestEvent.class})
    public void processApplicationEvent(DemoEvent event) {
        String msg = event.getMsg();
        System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
    }
}
事件过滤

如果希望通过一定的条件对事件进行过滤,可以使用 @EventListener 的 condition 属性。以下实例中只有 event 的 msg 属性是 my-event 时才会进行调用。

@EventListener(value = {DemoEvent.class, TestEvent.class}, condition = "#event.msg == 'my-event'")
public void processApplicationEvent(DemoEvent event) {
     String msg = event.getMsg();
     System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
 }

此时,发送符合条件的消息,listener 才会侦听到 publisher 发布的消息。

bean-listener 收到了 publisher 发布的消息: my-event

异步事件监听

前面提到的都是同步处理事件,那如果我们希望某个特定的侦听器异步去处理事件,如何做?

使用 @Async 注解可以实现类内方法的异步调用,这样方法在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。

@EventListener
@Async
public void processApplicationEvent(DemoEvent event) {
    String msg = event.getMsg();
    System.out.println("bean-listener 收到了 publisher 发布的消息: " + msg);
}

使用异步监听时,有两点需要注意:

  • 如果异步事件抛出异常,则不会将其传播到调用方。
  • 异步事件监听方法无法通过返回值来发布后续事件,如果需要作为处理结果发布另一个事件,请插入 ApplicationEventPublisher 以手动发布事件

3、好处及应用场景

ApplicationContext 在运行期会自动检测到所有实现了 ApplicationListener 的 bean,并将其作为事件接收对象。当我们与 spring 上下文交互触发 publishEvent 方法时,每个实现了 ApplicationListener 的 bean 都会收到 ApplicationEvent 对象,每个 ApplicationListener 可以根据需要只接收自己感兴趣的事件。

这样做有什么好处呢?

在传统的项目中,各个业务逻辑之间耦合性比较强,controller 和 service 间都是关联关系,然而,使用 ApplicationEvent 监听 publisher 这种方式,类间关系是什么样的?我们不如画张图来看看。

DemoPublisherDemoListener 两个类间并没有直接关联,解除了传统业务逻辑两个类间的关联关系,将耦合降到最小。这样在后期更新、维护时难度大大降低了。

Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!

ApplicationEvent 使用观察者模式实现,那什么时候适合使用观察者模式呢?观察者模式也叫 发布-订阅模式,例如,微博的订阅,我们订阅了某些微博账号,当这些账号发布消息时,我们都会收到通知。

总结来说,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,从而实现广播的效果。

4、源码阅读

Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!

Spring中的事件机制流程

1、ApplicationEventPublisher是Spring的事件发布接口,事件源通过该接口的pulishEvent方法发布事件

2、ApplicationEventMulticaster就是Spring事件机制中的事件广播器,它默认提供一个SimpleApplicationEventMulticaster实现,如果用户没有自定义广播器,则使用默认的。它通过父类AbstractApplicationEventMulticastergetApplicationListeners方法从事件注册表(事件-监听器关系保存)中获取事件监听器,并且通过invokeListener方法执行监听器的具体逻辑

3、ApplicationListener就是Spring的事件监听器接口,所有的监听器都实现该接口,本图中列出了典型的几个子类。其中RestartApplicationListnener在SpringBoot的启动框架中就有使用

4、在Spring中通常是ApplicationContext本身担任监听器注册表的角色,在其子类AbstractApplicationContext中就聚合了事件广播器ApplicationEventMulticaster和事件监听器ApplicationListnener,并且提供注册监听器的addApplicationListnener方法

通过上图就能较清晰的知道当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher发布事件,然后事件广播器ApplicationEventMulticaster会去事件注册表ApplicationContext中找到事件监听器ApplicationListnener,并且逐个执行监听器的onApplicationEvent方法,从而完成事件监听器的逻辑。

来到ApplicationEventPublisher 的 publishEvent 方法内部

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
 if (this.earlyApplicationEvents != null) {
 this.earlyApplicationEvents.add(applicationEvent);
 }
 else {
  //
  getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
 }
}

多播事件方法

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 Executor executor = getTaskExecutor();
 // 遍历所有的监听者
 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  if (executor != null) {
   // 异步调用监听器
   executor.execute(() -> invokeListener(listener, event));
  }
  else {
   // 同步调用监听器
   invokeListener(listener, event);
  }
 }
}

invokeListener

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
 ErrorHandler errorHandler = getErrorHandler();
 if (errorHandler != null) {
  try {
   doInvokeListener(listener, event);
  }
  catch (Throwable err) {
   errorHandler.handleError(err);
  }
 }
 else {
  doInvokeListener(listener, event);
 }
}

doInvokeListener

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
 try {
  // 这里是事件发生的地方
  listener.onApplicationEvent(event);
 }
 catch (ClassCastException ex) {
  ......
 }
}

点击 ApplicationListener 接口 onApplicationEvent 方法的实现,可以看到我们重写的方法。

Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!

5、总结

Spring 使用反射机制,获取了所有继承 ApplicationListener 接口的监听器,在 Spring 初始化时,会把监听器都自动注册到注册表中。

Spring 的事件发布非常简单,我们来总结一下:

  • 定义一个继承 ApplicationEvent 的事件
  • 定义一个实现 ApplicationListener 的监听器或者使用 @EventListener 监听事件
  • 定义一个发送者,调用 ApplicationContext 直接发布或者使用 ApplicationEventPublisher 来发布自定义事件

最后,发布-订阅模式可以很好的将业务逻辑进行解耦(上图验证过),大大提高了可维护性、可扩展性。

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!文章来源地址https://www.toymoban.com/news/detail-749994.html

到了这里,关于Spring Event 业务解耦神器,大大提高可扩展性,好用到爆!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于redisson实现延时队列解耦业务

          今天跟大家分享的是一个基于redisson实现的延时队列,有个初版的封装工具,使用者只用关心延时时间到了取到的数据处理(或者之前处理,到时间只做剩下的业务),废话不多说,直接上货。       这里是对物联网设备做数据模拟上报。看下原型转化后的需求界

    2024年02月11日
    浏览(36)
  • 基于 Webpack5 Module Federation 的业务解耦实践

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 本文作者:贝儿 本文中会提到很多目前数栈中使用的特定名词,统一做下解释描述 dt-common:每个子产品都会引入的公共包(类似 NPM 包

    2024年02月05日
    浏览(53)
  • 一键全自动剪辑,喝杯咖啡的时间,几百个视频就剪好了,效率大大提高,可以让我有更多的时间去研究剪辑的思路

    一键全自动剪辑:提升效率,释放创造力 在当今的快节奏数字时代,视频剪辑已经成为各行各业不可或缺的一部分。从社交媒体到电子商务,从教育到娱乐,视频剪辑的应用越来越广泛。然而,剪辑工作往往需要大量的时间和精力,如何提高效率成为了关键。本文将介绍一键

    2024年02月06日
    浏览(41)
  • 5款办公神器软件推荐:提高效率,享受分享

    给大家分享一些优秀的软件工具,是一件让人很愉悦的事情,今天继续带来5款优质软件。 PicGo是一款图床管理工具,可以快速上传图片到各种图床,并生成链接。你可以使用PicGo来管理你的图片资源,或者作为Markdown编辑器的辅助工具。PicGo支持多种图床,如七牛云、腾讯云、

    2023年04月19日
    浏览(51)
  • 【Spring源码】Spring Event事件

    目录 1、前言 2、什么是Spring Event? 3、基本使用 3.1、定义事件 3.2、发布事件 3.3、监听事件 3.3.1、继承ApplicationListener 3.3.2、使用@EventListener注解 4、Spring Event是同步还是异步? 4.1、源码实现 4.2、如何实现异步 4.2.1、使用@Async注解 4.2.2、手动实现异步线程池 4.2.3、自定义Appli

    2024年02月04日
    浏览(34)
  • 【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合!

    🎉🎉 欢迎光临,终于等到你啦 🎉🎉 🏅我是 苏泽 ,一位对技术充满热情的探索者和分享者。🚀🚀 🌟持续更新的专栏 《Spring 狂野之旅:从入门到入魔》 🚀 本专栏带你从Spring入门到入魔   这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://suzee.blog.

    2024年03月10日
    浏览(49)
  • Spring MVC Http Event Stream

    Event Stream 技术是一种实现服务器推送事件的方法,它通过在一个持续的 HTTP 连接上发送事件流来实现推送。具体来说,服务器发送一些事件到客户端,并将这些事件封装成一些指定格式的文本流。客户端通过监听这个流,就能实时收到服务器推送的事件。 text/event-stream 是一

    2024年02月09日
    浏览(37)
  • 低代码核心能力表单引擎可以提高业务处理效率,降低成本的

    在数字化时代,企业面临着海量的数据和复杂的业务需求,对于低代码表单的需求也逐渐增加,低代码表单可以提高企业的业务处理效率,还可以降低开发成本,缩短开发周期。 低代码的表单主要用于数据采集、流程审批和业务运营等场景,可以帮助企业更高效地管理数据和

    2024年02月04日
    浏览(42)
  • Spring Boot 整合 SSE(Server Sent Event)

    服务器发送事件(Server-Sent Events),简称 SSE。这是一种服务器端到客户端的单向消息推送。SSE 基于 HTTP 协议的,SSE 在服务器和客户端之间打开一个单向通道,服务端响应的不再是一次性的数据包而是text/event-stream类型的数据流信息 后端代码: 细节: 创建SseEmitter 对象时需要返

    2024年02月16日
    浏览(49)
  • 为业务数据库注入低代码智能:提高开发效率与灵活性的关键实践

    随着公司试图减少工作量并让他们的业务上线,很明显,大多数产品创意和功能需求都可以通过以最少的手动编码构建的可立即运行但可定制的应用程序进行分类。借助迎合公民开发人员的低代码开发平台,低代码开发成为过度劳累的 IT 部门新的具有成本效益的速赢方法。

    2024年02月20日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包