Spring Boot - Application Events 同步 VS 异步 发布订阅事件实战

news2024/11/17 9:25:41

文章目录

  • Pre
  • Code
  • 基础工程
    • 启动类
    • 切入口
    • 事件
  • 发布事件
  • 同步 Listener
  • 异步Listener
    • 增加@EnableAsync
    • 增加 @Async
  • 测试

在这里插入图片描述


Pre

Spring Boot - Application Events 的发布顺序_ApplicationStartingEvent

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent

Spring Boot - Application Events 的发布顺序_ApplicationContextInitializedEvent

Spring Boot - Application Events 的发布顺序_ApplicationPreparedEvent

Spring Boot - Application Events 的发布顺序_ContextRefreshedListener

Spring Boot - Application Events 的发布顺序_ApplicationStartedEvent

Spring Boot - Application Events 的发布顺序_AvailabilityChangeEvent

Spring Boot - Application Events 的发布顺序_ApplicationReadyEvent

Spring Boot - Application Events 的发布顺序_ApplicationFailedEvent

Spring Boot - ApplicationRunner && CommandLineRunner扩展接口


Code

在这里插入图片描述


基础工程

启动类

@SpringBootApplication 
public class LifeCycleApplication {

    /**
     * 除了手工add , 在 META-INF下面 的 spring.factories 里增加
     * org.springframework.context.ApplicationListener=自定义的listener 也可以
     *
     * @param args
     */
    public static void main(String[] args) {
        SpringApplication.run(LifeCycleApplication.class, args);

	}
}

META-INF中增加 spring.factories文件

org.springframework.context.ApplicationListener=\
com.artisan.practise.listeners.SpringBuiltInEventsListener

切入口

package com.artisan.practise.listeners;

import com.artisan.practise.publish.Publisher;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.SpringApplicationEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class SpringBuiltInEventsListener implements ApplicationListener<SpringApplicationEvent>, ApplicationContextAware {

    private ApplicationContext applicationContext;

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

    @Override
    public void onApplicationEvent(SpringApplicationEvent event) {
        System.out.println("SpringApplicationEvent Received - " + event);

        // Initializing publisher for custom event
        this.initPublisher(event);
    }

    private void initPublisher(SpringApplicationEvent event) {
        if (event instanceof ApplicationReadyEvent) {
            this.applicationContext.getBean(Publisher.class).publishEvent();
        }
    }
}
    

事件

package com.artisan.practise.events;

import org.springframework.context.ApplicationEvent;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class UserCreatedEvent extends ApplicationEvent {

	private static final long serialVersionUID = 1L;
	private String name;

	public UserCreatedEvent(Object source, String name) {
		super(source);
		this.name = name;
	}

	public String getName() {
		return this.name;
	}

}

package com.artisan.practise.events;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class UserRemovedEvent {

	public String name;

	public UserRemovedEvent(String name) {
		this.name = name;
	}

	public String getName() {
		return this.name;
	}

}



发布事件

package com.artisan.practise.publish;

import com.artisan.practise.events.UserCreatedEvent;
import com.artisan.practise.events.UserRemovedEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Component
public class Publisher {


    @Autowired
    private ApplicationEventPublisher publisher;

    public void publishEvent() {
        System.out.println("========================"  + Thread.currentThread().getName());
        // Async Event
        publisher.publishEvent("I'm Async");

        // @EventListener Annotated and ApplicationListener
        publisher.publishEvent(new UserCreatedEvent(this, "Artisan"));
        publisher.publishEvent(new UserRemovedEvent("Artisan"));

        // Conditional Listener
        publisher.publishEvent(new UserCreatedEvent(this, "reflectoring"));
        publisher.publishEvent(new UserRemovedEvent("reflectoring"));

    }

}
    

同步 Listener

package com.artisan.practise.listeners;

import com.artisan.practise.events.UserCreatedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Component
class UserCreatedListener implements ApplicationListener<UserCreatedEvent> {

	@Override
	public void onApplicationEvent(UserCreatedEvent event) {
		System.out.println(String.format("%s - User created: %s",Thread.currentThread().getName() , event.getName()));
	}
}

package com.artisan.practise.listeners;

import com.artisan.practise.events.UserRemovedEvent;
import com.artisan.practise.response.ReturnedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
@Component
public class UserRemovedListener {

    @EventListener
    ReturnedEvent handleUserRemovedEvent(UserRemovedEvent event) {
        System.out.println(String.format("%s - User removed (@EventListerner): %s", Thread.currentThread().getName(), event.getName()));
        // Spring will send ReturnedEvent as a new event
        return new ReturnedEvent();
    }

    // Listener to receive the event returned by Spring
    @EventListener
    void handleReturnedEvent(ReturnedEvent event) {
        System.out.println(String.format("%s - ReturnedEvent received.", Thread.currentThread().getName()));
    }

    @EventListener(condition = "#event.name eq 'reflectoring'")
    void handleConditionalListener(UserRemovedEvent event) {
        System.out.println(String.format("%s - User removed (Conditional): %s", Thread.currentThread().getName(), event.getName()));
    }

    @TransactionalEventListener(condition = "#event.name eq 'reflectoring'", phase = TransactionPhase.AFTER_COMPLETION)
    void handleAfterUserRemoved(UserRemovedEvent event) {
        System.out.println(String.format("%s - User removed (@TransactionalEventListener): %s", Thread.currentThread().getName(), event.getName()));
    }

}


异步Listener

增加@EnableAsync

启动类增加@EnableAsync

@SpringBootApplication
@EnableAsync
public class LifeCycleApplication {



}

@EnableAsync 是一个在 Spring 框架中使用的注解,它用于启用 Spring 的异步执行功能。当在一个配置类上加上 @EnableAsync 注解时,Spring 容器会设置异步任务执行的支持。这允许你将任务标记为异步,并且可以在不同的线程中执行它们,从而提高应用程序的响应能力和吞吐量。
以下是一些关键点,用以解释 @EnableAsync 注解的功能和用法:

  1. 异步执行: 在 Spring 应用中,你可以使用 @Async 注解来标记一个方法为异步执行。当方法被调用时,它将在一个单独的线程中运行,而不是在调用线程中立即执行。

  2. 启用异步执行: 为了使 @Async 注解生效,必须在 Spring 应用程序的配置中启用异步支持。这通常是通过在 Spring 配置类上添加 @EnableAsync 注解来实现的。

  3. 线程池: @EnableAsync 注解允许你定义一个自定义的线程池,Spring 会使用这个线程池来执行异步任务。如果你没有提供线程池,Spring 会使用默认的线程池。

  4. 异常处理: 异步方法执行中发生的异常通常不会传递给调用者。@EnableAsync 支持异常处理配置,允许你定义如何处理这些异常。

  5. 调度器: 你可以指定一个 TaskScheduler Bean,Spring 使用它来调度异步任务。默认情况下,Spring 容器会创建一个 SimpleAsyncTaskExecutor 实例。

  6. 顺序执行: 如果你需要确保某些异步方法按照严格的顺序执行,可以使用 @Async 注解的 dependsOn 属性来指定依赖关系。

使用 @EnableAsync 注解可以让开发者轻松地构建高并发的应用程序,提高应用程序处理大量并发请求的能力,同时保持代码的清晰和易管理性。在微服务架构和分布式系统中,异步通信是提高系统解耦和性能的关键技术之一。


增加 @Async

增加 @Async

package com.artisan.practise.listeners;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
class AsyncListener {

    @Async
    @EventListener
    void handleAsyncEvent(String event) {
        System.out.println(String.format("%s - Async event recevied: %s  ",  Thread.currentThread().getName(), event));
    }

}

@Async 是一个方法级别的注解,在 Spring 框架中用于标识一个方法应该以异步方式执行。当一个方法被标记为 @Async 时,它将在一个单独的线程中运行,而不是在调用它的线程中立即执行。这种方式可以避免阻塞调用线程,从而提高应用程序的响应能力和吞吐量。
以下是一些关于 @Async 注解的关键点:

  1. 异步方法执行: @Async 注解允许你将方法的执行放到一个单独的线程中,这样主线程就可以立即返回,继续处理其他任务。
  2. 线程池: @Async 注解通常与 @EnableAsync 注解一起使用,后者启用了异步支持并定义了一个线程池。异步方法通常会在这个线程池中分配线程来执行。
  3. 异常处理: 异步方法执行过程中出现的异常通常不会传递给调用者。但是,可以通过配置 AsyncUncaughtExceptionHandler 来处理这些异常。
  4. 调度器: @Async 注解可以指定一个 TaskScheduler Bean,它负责调度异步任务的执行。如果没有指定,Spring 会默认使用一个 SimpleAsyncTaskExecutor
  5. 顺序执行: 如果需要确保某些异步方法按照严格的顺序执行,可以使用 @Async 注解的 dependsOn 属性来指定依赖关系。
  6. 触发器: @Async 方法可以由其他 @Async 方法触发,这允许创建异步的工作流和回调。
  7. 注解兼容性: @Async 注解可以与 @Transactional 注解一起使用,但是需要确保事务性注解和异步注解在方法上的使用是兼容的。

异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇

异步编程 - 09 Spring框架中的异步执行_@Async注解异步执行原理&源码解析


测试

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

低代码自动化测试的实践

何为低代码测试 传统上&#xff0c;功能、 UI、端到端等测试自动化的实现都涉及编写测试脚本&#xff0c;代替测试人员执行重复的手动测试任务。自动化脚本的开发工作通常由 QA 工程师或开发人员完成&#xff0c;这需要编写大量代码。 而低代码甚至无代码的理念也是在自动化测…

SpringBoot+SSM项目实战 苍穹外卖(12) Apache POI

继续上一节的内容&#xff0c;本节是苍穹外卖后端开发的最后一节&#xff0c;本节学习Apache POI&#xff0c;完成工作台、数据导出功能。 目录 工作台Apache POI入门案例 导出运营数据Excel报表 工作台 工作台是系统运营的数据看板&#xff0c;并提供快捷操作入口&#xff0c…

SQLServer 为角色开视图SELECT权限,报错提示需要开基础表权限

问题&#xff1a; 创建了个视图V&#xff0c;里面包含V库的a表&#xff0c;和T库的b表 为角色开启视图V的SELECT权限&#xff0c;提示T库的b表无SELECT权限&#xff0c;报错如下 解决方案&#xff1a; ①在T库建个视图TV&#xff0c;里面包含b表&#xff08;注意是在b表的对…

《2023年度程序员收入报告》 :旧金山位居第一,北京程序员中位数超60万元

2024年刚刚拉开序幕&#xff0c;备受瞩目的程序员薪资调研报告再度登场。由知名数据采集平台levels.fyi 搜集并整理了《2023年全球程序员收入报告》&#xff0c;为我们揭示了程序员最新的收入情况&#xff0c;其中有哪些值得关注的亮点呢&#xff1f; 行情向好&#xff0c;大多…

MS8257N超低噪声、宽带、可选反馈电阻跨阻放大器

产品简述 MS8257N 是一颗宽带、快速过载恢复时间、快速建立时 间、跨阻增益可调、超低噪声的跨阻放大器&#xff0c;主要用于光电 监测和各种高性能的光电系统。快速过载恢复特性和内部输 入保护电路可以让信号从过载传输中快速恢复正常。两档可 选跨阻增益保证了极高的动…

Matlab深度学习进行波形分割(二)

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 &#x1f510;#### 防伪水印——左手の明天 ####&#x1f510; &#x1f497; 大家…

Ubuntu 22.04 安装prometheus

服务器监控和报警软件有很多&#xff0c;为什么我们会选择Prometheus而不是其他软件呢&#xff1f; 因为它有以下优点&#xff1a; 自带简易web监控页面&#xff0c;用户可以很方便地查看监控数据和使用仪表盘。能实时收集数据并根据自定义警报规则推送告警&#xff1b;具有丰…

企业如何找到合适的内容策略?媒介盒子分享

企业如果想要抢先占领用户心智的话&#xff0c;媒介盒子认为首先需要找到合适的内容策略&#xff0c;好的内容能够与消费者建立双向信任的关系&#xff0c;一种让消费者对品牌的好感度提升&#xff0c;进而成为品牌的忠实用户&#xff0c;接下来媒介盒子就来和大家聊聊&#xf…

Selenium Grid - 多台计算机上并行运行

当你希望在多台计算机上并行运行测试&#xff1f;Selenium Grid可以帮你实现。 官方文档原文&#xff1a; https://www.selenium.dev/documentation/grid/getting_started/ Selenium Grid允许通过将客户端发送的命令路由到远程浏览器实例&#xff0c;在远程机器上执行WebDriv…

mac上搭建 hadoop 伪集群

1. hadoop介绍 Hadoop是Apache基金会开发的一个开源的分布式计算平台&#xff0c;主要用于处理和分析大数据。Hadoop的核心设计理念是将计算任务分布到多个节点上&#xff0c;以实现高度可扩展性和容错性。它主要由以下几个部分组成&#xff1a; HDFS (Hadoop Distributed Fi…

Kafka生产消费流程

Kafka生产消费流程 1.Kafka一条消息发送和消费的流程图(非集群) 2.三种发送方式 准备工作 创建maven工程&#xff0c;引入依赖 <dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>3.3.1…

【MATLAB】 HANTS滤波算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~ 1 基本定义 HANTS滤波算法是一种时间序列谐波分析方法&#xff0c;它综合了平滑和滤波两种方法&#xff0c;能够充分利用遥感图像存在时间性和空间性的特点&#xff0c;将其空间上的分布规律和时间上的变化规律联系起来…

【MySQL性能优化】- MySQL结构与SQL执行过程

MySQL结构与SQL执行过程 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &#x1f31d;分享学习心得&#xff0c;欢迎指正…

BPMN 2.0 相关定义概要描述

官方文档&#xff1a;https://www.omg.org/spec/BPMN/2.0/ BPMN 2.0 &#xff08;BPMN&#xff0c;业务流程模型和标记&#xff0c;Business Process Model And Notation&#xff09; 是一种业务流程建模和执行的标准&#xff0c;它使用 XML 格式来描述业务流程。 以下是 BPM…

Eureka 本机集群实现

距离上次发布博客已经一年多了&#xff0c;主要就是因为考研&#xff0c;没时间学习技术的内容&#xff0c;现在有时间继续完成关于代码方面的心得&#xff0c;希望跟大家分享。 今天在做一个 Eureka 的集群实现&#xff0c;我是在本电脑上跑的&#xff0c;感觉这个挺有意思&a…

alibaba.item_get API:电商行业中的数据驱动决策支持

alibaba.item_get API 是阿里巴巴提供的一个用于获取商品详情的接口。在电商行业中&#xff0c;数据驱动的决策支持是非常重要的&#xff0c;而这个 API 可以帮助你获取到商品的各种详细信息&#xff0c;从而为你的决策提供支持。 具体来说&#xff0c;通过使用 alibaba.item_…

可以部署到Vercel的一些有趣项目

博客地址 可以部署到Vercel的一些有趣项目-雪饼分享几款可以部署在Vercel上的项目&#xff0c;更新中~ 免费的域名要不要&#xff1f; 如果你还不会将项目部署到Vercel&#xff0c;或是绑定域名建议阅读 将项目部署到Vercel&#xff0c;并绑定域名 Excalidraw 白板 一个开源的…

Netty通信中的粘包半包问题(二)

在前面我们已经分析过Netty会出现的粘包半包问题&#xff0c;还没看过前面的博客的&#xff0c;可以先去看下之前写的博客 Netty通信中的粘包半包问题(一) 解放方式:特殊分隔符解决,在每个报文后面加上一个特殊分隔符&#xff0c;以此来告诉服务端每个报文的数据结界是什么 1.…

RIP【新华三与华为区别】

【介绍】 rip分为rip 1 与 rip 2 &#xff0c;rip 2 是对 rip 1 的一种升级&#xff0c;rip 2 可以进行认证等功能 【命令】 新华三&#xff1a; [HC3-R1] rip #启用rip [HC3-R1-rip] version 2 #告知rip 版本号 [HC3-R1-rip] network 192.168.1.0 #宣告其网段 [HC3-R1-rip] …

13、Redis高频面试题

1、项目中为什么用Redis 我们项目中之所以选择Redis&#xff0c;主要是因为Redis有下面这些优点&#xff1a; 操作速度快&#xff1a;Redis的数据都保存在内存中&#xff0c;相比于其它硬盘类的存储&#xff0c;速度要快很多数据类型丰富&#xff1a;Redis支持 string&#x…