当前位置: 首页 > 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开启功能

 

http://www.lryc.cn/news/321764.html

相关文章:

  • sadtalker-api/
  • vue+elementUI实现指定列的单元格可编辑
  • RK3568平台开发系列讲解(基础篇)内核是如何发送事件到用户空间
  • 力扣---打家劫舍---动态规划
  • mac安装rust环境
  • 1058:求一元二次方程
  • GraphQL入门之一对多关联查询
  • MATLAB和Python数值和符号计算可视化物理学气体动能和粒子速度
  • 阿里云-零基础入门NLP【基于机器学习的文本分类】
  • 蓝桥杯模块综合——高质量讲解AT24C02,BS18B20,BS1302,AD/DA(PCF8591),超声波模块
  • 前端跨平台开发框架:简化多端开发的利器
  • cesium.js加载模型后,重新设置旋转角度属性值
  • ②免费AI软件开发工具测评:通义灵码 VS 码上飞
  • 幻兽帕鲁游戏搭建(docker)
  • unity报错出现Asset database transaction committed twice!
  • 去除项目git的控制 端口号的关闭
  • 交叉注意力融合时域、频域特征的FFT + CNN -BiLSTM-CrossAttention电能质量扰动识别模型
  • 简单的Charles抓包教程
  • 如何构建Docker自定义镜像
  • 一起学数据分析_2
  • 请解释Redis是什么?它有哪些主要应用场景?Redis支持哪些数据类型?并描述每种数据类型的特性和使用场景。
  • 在centos8中部署Tomcat和Jenkins
  • 机器学习模型—K means
  • QT UI设计
  • 前端小白的学习之路(CSS3 一)
  • 春暖花开,一起来看看2024年品牌春分海报吧!
  • golang面试题总结
  • BUGKU-WEB shell
  • 系统重构后,对项目定制开发的兼容性问题
  • Linux---基本操作命令之用户管理命令