当前位置: 首页 > 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;自定义构建一个镜像。 开…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案

引言 在分布式系统的事务处理中&#xff0c;如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议&#xff08;2PC&#xff09;通过准备阶段与提交阶段的协调机制&#xff0c;以同步决策模式确保事务原子性。其改进版本三阶段提交协议&#xff08;3PC&#xf…...

解析“道作为序位生成器”的核心原理

解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制&#xff0c;重点解析"道作为序位生成器"的核心原理与实现框架&#xff1a; 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...