当前位置: 首页 > news >正文

SpringBoot3框架,事件和监听器、SPI

事件和监听器

生命周期监听

自定义监听器的步骤:

  1. 编写SpringApplicationRunListener实现类(各个实现方法的功能写在其sout内)

    public class MyAppListener implements SpringApplicationRunListener {@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {System.out.println("正在启动");}@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {System.out.println("环境准备完成");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("ioc容器准备完成");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("ioc容器加载完成");}@Overridepublic void started(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println("启动完成");}@Overridepublic void ready(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println("应用准备就绪");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("应用启动失败");}
    }
    
  2. META-INF/spring.factories 中配置 org.springframework.boot.SpringApplicationRunListener=自定义listener的全限定符,还可以指定一个有参构造器,接受两个参数(SpringApplication application, String[] args)

    org.springframework.boot.SpringApplicationRunListener=com.ergou.boot3.listener.MyAppListener
    

以上监听器执行流程

Listener先要从 META-INF/spring.factories 读到

  1. 引导: 利用 BootstrapContext 引导整个项目启动
    1. starting:应用开始,SpringApplication的run方法一调用,只要有了 BootstrapContext 就执行
    2. environmentPrepared:环境准备好(把启动参数等绑定到环境变量中),但是ioc还没有创建;【调一次】
  2. 启动:
    1. contextPrepared:ioc容器创建并准备好,但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建 【调一次】
    2. contextLoaded: ioc容器加载。主配置类加载进去了。但是ioc容器还没刷新(bean没创建) =======截止以前,ioc容器里面还没造bean=======
    3. started: ioc容器刷新了(所有bean造好了),但是 runner 没调用。
    4. ready: ioc容器刷新了(所有bean造好了),所有 runner 调用完了。
  3. 运行: 以前步骤都正确执行,代表容器running。如果不能正常运行(以上的六个步骤有出现错误),调用failed方法。

回调监听器

回调监听器用于感知项目的生命周期的事件

  • BootstrapRegistryInitializer感知特定阶段:感知引导初始化
    • META-INF/spring.factories 配置
    • 创建引导上下文bootstrapContext的时候触发。
  • ApplicationContextInitializer感知特定阶段: 感知ioc容器初始化
    • META-INF/spring.factories 配置
  • ApplicationListener: 感知全阶段:基于事件机制,感知事件。 一旦到了哪个阶段可以做别的事
    • META-INF/spring.factories
  • SpringApplicationRunListener: 感知全阶段生命周期 + 各种阶段都能自定义操作。功能更完善。
    • META-INF/spring.factories
  • ApplicationRunner: 感知特定阶段:感知应用就绪Ready。应用启动失败,就不会就绪
    • @Bean配置
  • CommandLineRunner: 感知特定阶段:感知应用就绪Ready。应用启动失败,就不会就绪
    • @Bean配置

配置步骤:

  1. 自定义监听器,实现相应的监听器接口,重写相应方法,例:

    public class MyListener2 implements ApplicationListener<ApplicationEvent> {@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println("感知到事件:"+event);}
    }
    
  2. 配置监听器,例:

    org.springframework.context.ApplicationListener=com.ergou.boot3.ssm.listener.MyListener2
    

建议:

  • 如果项目启动前做事: BootstrapRegistryInitializerApplicationContextInitializer
  • 如果想要在项目启动完成后做事:ApplicationRunnerCommandLineRunner
  • 如果要干涉生命周期做事:SpringApplicationRunListener
  • 如果想要用事件机制:ApplicationListener

9大事件触发顺序&时机

  1. ApplicationStartingEvent:应用启动但未做任何事情, 除过注册listeners and initializers.
  2. ApplicationEnvironmentPreparedEvent: Environment 准备好,但context 未创建.
  3. ApplicationContextInitializedEvent: ApplicationContext 准备好,ApplicationContextInitializers 调用,但是任何bean未加载
  4. ApplicationPreparedEvent: 容器刷新之前,bean定义信息加载
  5. ApplicationStartedEvent: 容器刷新完成, runner未调用

=========以下就开始插入了探针机制============

  1. AvailabilityChangeEventLivenessState.CORRECT应用存活; 存活探针
  2. ApplicationReadyEvent: 任何runner被调用
  3. AvailabilityChangeEventReadinessState.ACCEPTING_TRAFFIC就绪探针,可以接请求
  4. ApplicationFailedEvent :启动出错

事件驱动开发

事件驱动开发步骤:

  1. 首先创建一个事件的发布者EventPublisher类,这个类要实现ApplicationEventPublisherAware,springboot会通过ApplicationEventPublisherAware接口自动注入,接着实现setApplicationEventPublisher方法,并且自定义一个方法来调用底层API发送事件,事件是广播出去的。所有监听这个事件的监听器都可以收到

  2. 我们要自定义一个登录成功事件LoginSuccessEvent,这个事件用来绑定用户User类,并且被该功能模块下的service调用

  3. 接下来我们要在service的功能代码使用@EventListener注解来进行订阅事件

  4. 最后在controller中进行发送事件,相当于原始的调用service功能方法

  5. 创建事件发布者

    @Service
    public class EventPublisher implements ApplicationEventPublisherAware {/*** 底层发送事件用的组件,springboot会通过ApplicationEventPublisherAware接口自动注入给我们* 事件是广播出去的。所有监听这个事件的监听器都可以收到* */ApplicationEventPublisher applicationEventPublisher;/*** 所有事件都可以发送* */public void sendEvent(ApplicationEvent event){//调用底层API发送事件applicationEventPublisher.publishEvent(event);}//会被自动调用,把真正发送事件的底层组件注入@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}
    }
  6. 创建功能事件

    public class LoginSuccessEvent extends ApplicationEvent {/*** 代表是谁成功登录了* */public LoginSuccessEvent(User user) {super(user);}
    }
  7. 在service层中订阅相应事件,并做出相应业务处理

    @Service
    public class CouponService {//当loginSuccessEvent事件发生时,@EventListener标注的方法会自动执行,称为订阅@EventListenerpublic void onEvent(LoginSuccessEvent loginSuccessEvent){System.out.println("=======CouponService ======感知到事件"+loginSuccessEvent);User source = (User) loginSuccessEvent.getSource();sendCoupon(source.getUsername());}public void sendCoupon(String username){System.out.println(username+"随机收到了一张优惠券");}}
    
  8. 最后在controller层发送相应的事件即可

    @RestController
    public class LoginController {@Autowiredprivate EventPublisher eventPublisher;@GetMapping("/login")public String login(@RequestParam("username") String username,@RequestParam("password")String password){//业务处理登录System.out.println("业务处理登录完成....");User user = new User(username, password);//TODO 发送事件LoginSuccessEvent loginSuccessEvent = new LoginSuccessEvent(user);eventPublisher.sendEvent(loginSuccessEvent);//设计模式:对新增开发,对修改关闭return username+"登录成功";}
    }

自动配置原理回顾:

  1. 导入starter
  2. 依赖导入autoconfigure
  3. 寻找类路径下 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
  4. 启动,加载所有 自动配置类 xxxAutoConfiguration
    1. 给容器中配置功能组件
    2. 组件参数绑定到 属性类中。xxxProperties
    3. 属性类配置文件前缀项绑定
    4. @Contional派生的条件注解进行判断是否组件生效
  5. 效果:
    1. 修改配置文件,修改底层参数
    2. 所有场景自动配置好直接使用
    3. 可以注入SpringBoot配置好的组件随时使用

SPI机制

  • Java中的SPI(Service Provider Interface)是一种软件设计模式,用于在应用程序中动态地发现和加载组件。SPI的思想是,定义一个接口或抽象类,然后通过在classpath中定义实现该接口的类来实现对组件的动态发现和加载。
  • SPI的主要目的是解决在应用程序中使用可插拔组件的问题。例如,一个应用程序可能需要使用不同的日志框架或数据库连接池,但是这些组件的选择可能取决于运行时的条件。通过使用SPI,应用程序可以在运行时发现并加载适当的组件,而无需在代码中硬编码这些组件的实现类。
  • 在Java中,SPI的实现方式是通过在META-INF/services目录下创建一个以服务接口全限定名为名字的文件,文件中包含实现该服务接口的类的全限定名。当应用程序启动时,Java的SPI机制会自动扫描classpath中的这些文件,并根据文件中指定的类名来加载实现类。
  • 通过使用SPI,应用程序可以实现更灵活、可扩展的架构,同时也可以避免硬编码依赖关系和增加代码的可维护性。

在SpringBoot中,通过

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

来进行SPI

关于配置:

  • 自动配置:全部都配置好,什么都不用管。 自动批量导入
    • 项目一启动,spi文件中指定的所有都加载。
  • @EnableXxxx:手动控制哪些功能的开启; 手动导入。
    • 开启xxx功能
    • 利用 @Import 把此功能要用的组件导入进去

@SpringBootApplication注解及其相关注解

@SpringBootConfiguration

作用与@Configuration一致,容器中的组件,配置类。spring ioc启动就会加载创建这个类的组件

@EnableAutoConfiguration

开启自动配置

@AutoConfigurationPackage

  • 利用@Import(AutoConfiguration.Registrar.class)给容器中导入想要的组件
  • 把主程序所在的包的所有组件导入进来

@Import(AutoConfigurationImportSelector.class)

加载所有自动配置类(扫描SPI文件:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@ComponentScan

组件扫描:排除一些组件(排除前面已经扫描过的配置类和自动配置类)

生命周期启动加载机制

自定义starter

例如:

场景:抽取聊天机器人场景,它可以打招呼

效果:任何项目导入此starter都具有打招呼功能,并且问候语中的人名需要可以在配置文件中修改

创建自定义starter项目,引入spring-boot-starter基础依赖

编写模块功能,引入模块所有需要的依赖。

编写xxxAutoConfiguration自动配置类,帮其他项目导入这个模块需要的所有组件

编写配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports指定启动需要加载的自动配置

其他项目引入即可使用

自定义的starter的配置方式还可以使用@EnableXxxx的方式

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(RobotAutoConfiguration.class)
public @interface EnableRobot {}

如此,别人引入starter需要使用 @EnableRobot开启功能

 

相关文章:

SpringBoot3框架,事件和监听器、SPI

事件和监听器 生命周期监听 自定义监听器的步骤&#xff1a; 编写SpringApplicationRunListener实现类&#xff08;各个实现方法的功能写在其sout内&#xff09; public class MyAppListener implements SpringApplicationRunListener {Overridepublic void starting(Configu…...

sadtalker-api/

———— 下载sadtalker工程文件&#xff0c;包括844个模型 。。。。。。。。。。。。。。。。 配置环境&#xff1a; pip源&#xff0c;设置&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple anaconda prompt, 进入命令行 how在 …...

vue+elementUI实现指定列的单元格可编辑

template中的代码如下&#xff1a; <div v-if"(item.label 高压侧 || item.label 低压侧)&&coloumnHeader.label 单柱片数"><div class"editableCell"><div v-if"item.label 高压侧" dblclick"changeValue(sco…...

RK3568平台开发系列讲解(基础篇)内核是如何发送事件到用户空间

🚀返回专栏总目录 文章目录 一、相关接口函数二、udevadm 命令三、实验沉淀、分享、成长,让自己和他人都能有所收获!😄 一、相关接口函数 kobject_uevent 是 Linux 内核中的一个函数, 用于生成和发送 uevent 事件。 它是 udev 和其他设备管理工具与内核通信的一种方式。…...

力扣---打家劫舍---动态规划

思路 1&#xff1a; 我将res[i]定义为&#xff1a;一定要取第 i 个房子的前提下&#xff0c;能获取的最大金额。那么直接用cnt从头记录到尾&#xff0c;每个房子的res最大值即是答案。那么递推公式是什么&#xff1f;res[i]max(res[i-2],res[i-1],...,res[0])nums[i]。数组初始…...

mac安装rust环境

mac安装rust环境 老规矩官方文档 1. mac官网使用的是脚本安装, 至于为啥没使用brew也没推荐俺也不太清楚 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh2. 一般来说中途会遇见有一个选择我这里选择直接回车默认安装(如果以后我研究明白的话会进行更新, 现在是…...

1058:求一元二次方程

【题目描述】 利用公式 求一元二次方程axbxc0的根&#xff0c;其中a不等于0。结果要求精确到小数点后5位。 【输入】 输入一行&#xff0c;包含三个浮点数a,b,c&#xff08;它们之间以一个空格分开&#xff09;&#xff0c;分别表示方程axbxc0的系数。 【输出】 输出一行&…...

GraphQL入门之一对多关联查询

创建 Node.js 的工程 mkdir myapp cd myapp npm init (一路回车)安装依赖包 npm install apollo/server graphql定义 Schema 创建 schema.graphql 文件&#xff0c;内容如下&#xff1a; type Book {title: String!author: Author! }type Author {name: String!books: [Boo…...

MATLAB和Python数值和符号计算可视化物理学气体动能和粒子速度

要点 Python物理学差分数值和符号计算 热动力学计算&#xff1a;统计力学&#xff0c;分子动力学模型 Python寻找弹性物体的运动&#xff0c;LAMMPS 分子动力学模拟器模拟2D气体分子&#xff0c;Python原子模拟绘图&#xff0c;Python数值计算原子平衡性&#xff0c;Python绘制…...

阿里云-零基础入门NLP【基于机器学习的文本分类】

文章目录 学习过程赛题理解学习目标赛题数据数据标签评测指标解题思路TF-IDF介绍TF-IDF 机器学习分类器TF-IDF LinearSVCTF-IDF LGBMClassifier 学习过程 20年当时自身功底是比较零基础(会写些基础的Python[三个科学计算包]数据分析)&#xff0c;一开始看这块其实挺懵的&am…...

蓝桥杯模块综合——高质量讲解AT24C02,BS18B20,BS1302,AD/DA(PCF8591),超声波模块

AT24C02——就是一个存储的东西&#xff0c;可以给他写东西&#xff0c;掉电不丢失。 void EEPROM_Write(unsigned char * EEPROM_String,unsigned char addr , unsigned char num) {IIC_Start();IIC_SendByte(0xA0);IIC_WaitAck();IIC_SendByte(addr);IIC_WaitAck();while(nu…...

前端跨平台开发框架:简化多端开发的利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

cesium.js加载模型后,重新设置旋转角度属性值

// 加载模型var position Cesium.Cartesian3.fromDegrees(longitude, latitude, height);// 计算矩阵var rollAngleDegrees 15; // 设置翻滚角度var rollAngleRadians Cesium.Math.toRadians(rollAngleDegrees); // 将角度转换为弧度var orientation Cesium.Transforms.eas…...

②免费AI软件开发工具测评:通义灵码 VS 码上飞

前言 我又双叒叕来测评了&#xff01;上次给大家带来的是iFlyCode和CodeFlying两款产品的测评&#xff0c;受到了大家的一致好评~ 今天咱就继续来聊聊&#xff0c;这次我们选的的对象是通义灵码和码上飞&#xff0c;从名字上也能看到出来这两款产品一定是跟软件开发有关系的&…...

幻兽帕鲁游戏搭建(docker)

系列文章目录 第一章&#xff1a; 幻兽帕陆游戏搭建 文章目录 系列文章目录前言一、镜像安装1.创建游戏目录2.拉取镜像3.下载配置文件4.启动游戏 二、自定义配置总结 前言 这段时间一直在写论文还有找工作&#xff0c;也没学啥新技术&#xff0c;所以博客也很长时间没写了&am…...

unity报错出现Asset database transaction committed twice!

错误描述&#xff1a; 运行时报错 Assertion failed on expression: ‘m_ErrorCode MDB_MAP_RESIZED || !HasAbortingErrors()’Asset database transaction committed twice!Assertion failed on expression: ‘errors MDB_SUCCESS || errors MDB_NOTFOUND’ 解决办法&…...

去除项目git的控制 端口号的关闭

以下操作都是在windows下。只是记录一下。 find . -name “.git” | xargs rm -rf 查看所有分支 git branch -a 查看当前分支 git branch -a 切换分支 git chenkout develop docker 查看容器的ip docker inspect -f ‘{{.Name}} - {{range .NetworkSettings.Networks}}{{.IP…...

交叉注意力融合时域、频域特征的FFT + CNN -BiLSTM-CrossAttention电能质量扰动识别模型

往期精彩内容&#xff1a; 电能质量扰动信号数据介绍与分类-Python实现-CSDN博客 Python电能质量扰动信号分类(一)基于LSTM模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(二)基于CNN模型的一维信号分类-CSDN博客 Python电能质量扰动信号分类(三)基于Transformer…...

简单的Charles抓包教程

安装Charles 安装地址&#xff1a;https://www.charlesproxy.com/download/ 开关本机抓包 一般我们在抓取手机端内容时需要将Proxy菜单栏下的Windows Proxy取消勾选&#xff0c;禁止charles抓取本机上的请求信息。 注&#xff1a;开启电脑端抓包后&#xff0c;会为电脑添加局…...

如何构建Docker自定义镜像

说明&#xff1a;平常我们使用Docker运行各种容器&#xff0c;极大地方便了我们对开发应用的使用&#xff0c;如MySQL、Redis&#xff0c;以及各种中间件&#xff0c;使用时只要拉镜像&#xff0c;运行容器即可。本文介绍如何创建一个Demo&#xff0c;自定义构建一个镜像。 开…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

WEB3全栈开发——面试专业技能点P4数据库

一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库&#xff0c;基于 mysql 库改进而来&#xff0c;具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点&#xff1a; 支持 Promise / async-await&#xf…...

Cursor AI 账号纯净度维护与高效注册指南

Cursor AI 账号纯净度维护与高效注册指南&#xff1a;解决限制问题的实战方案 风车无限免费邮箱系统网页端使用说明|快速获取邮箱|cursor|windsurf|augment 问题背景 在成功解决 Cursor 环境配置问题后&#xff0c;许多开发者仍面临账号纯净度不足导致的限制问题。无论使用 16…...

软件工程教学评价

王海林老师您好。 您的《软件工程》课程成功地将宏观的理论与具体的实践相结合。上半学期的理论教学中&#xff0c;您通过丰富的实例&#xff0c;将“高内聚低耦合”、SOLID原则等抽象概念解释得十分透彻&#xff0c;让这些理论不再是停留在纸面的名词&#xff0c;而是可以指导…...

基于微信小程序的作业管理系统源码数据库文档

作业管理系统 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和微信小程序来完成对系统的…...