SpringBoot3【⑤ 核心原理】

1. 事件和监听器

1. 生命周期监听

场景:监听应用的生命周期

1. 监听器-SpringApplicationRunListener

  1. 自定义SpringApplicationRunListener监听事件
    1.1. 编写SpringApplicationRunListener 这个接口的实现类
    1.2. 在 META-INF/spring.factories 中配置 org.springframework.boot.SpringApplicationRunListener=自己的Listener,还可以指定一个有参构造器,接受两个参数(SpringApplication application, String[] args)
    1.3. springboot 在spring-boot.jar中配置了默认的 Listener,如下

在这里插入图片描述
补充一下:Springboot老版本其实自动配置类的对应导入的kv键值对其实不在现在的 META-INF/spring/xxxxxx.Imports 下,而是 META-INF/spring.factories里面
在这里插入图片描述
在这里插入图片描述
雷神源码讲解spring主程序启动

public class MyAppListener implements SpringApplicationRunListener {@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {System.out.println("===========starting=============正在启动=======================");}@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {System.out.println("===========environmentPrepared=============环境准备完成=======================");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("===========contextPrepared=============ioc容器准备完成=======================");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("===========contextLoaded=============ioc容器加载完成=======================");}@Overridepublic void started(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println("===========started=============应用启动完成=======================");}@Overridepublic void ready(ConfigurableApplicationContext context, Duration timeTaken) {System.out.println("===========ready=============应用准备就绪=======================");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("===========failed=============应用启动失败=======================");}
}

源码的run方法,先进行引导步骤
在这里插入图片描述
源码可见当contextPrepared进行的时候,正是引导结束的时候,标志着ioc容器创建成功但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建
在这里插入图片描述
contextLoaded->主配置类加载但是没有刷新【说明Bean没创建,但是会加载Bean的定义信息,需要刷新才能把组件创建出来】(前几节讲过,可以从主程序的run()方法一路点,找到刷新容器的方法,必须经历spring容器刷新的12大步,才能把各种组件装到spring容器里面)
在这里插入图片描述

-----------------------------------------------------截至以前,ioc容器里面还没造bean-----------------------------------------------------

started: ioc容器刷新了(所有bean造好了),但是 runner 没调用。
在这里插入图片描述
可以在主程序的run方法里面看到started方法调用完了之后,就会调用callRunners方法,如果没有出现异常,就会依次调用listenerready方法

在这里插入图片描述
而在主程序run方法中其实可以发现,在starting之后,runner调用之前,出现的异常都被它捕获到,在此期间出现异常会调用handleRunFailue方法处理运行失败

在这里插入图片描述
而调用handleRunFailue方法的情况下,第一步就是如果存在listenners,就调用它的failed方法,至此,全部的方法执行位置和大致流程就看完了
在这里插入图片描述

原理——>

Listener先要从 META-INF/spring.factories 读到1、引导: 利用 BootstrapContext 引导整个项目启动starting:              应用开始,SpringApplication的run方法一调用,只要有了 BootstrapContext 就执行environmentPrepared:   环境准备好(把启动参数等绑定到环境变量中),但是ioc还没有创建;这里的【once】应该被翻译为【一旦】环境准备好,ioc容器还没创建之前 而不是【只会调一次】2、启动:contextPrepared:       ioc容器创建并准备好,但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建  【调一次】contextLoaded:         ioc容器加载。主配置类加载进去了。但是ioc容器还没刷新(我们的bean没创建)。=======截止以前,ioc容器里面还没造bean呢=======started:               ioc容器刷新了(所有bean造好了),但是 runner 没调用。ready:                  ioc容器刷新了(所有bean造好了),所有 runner 调用完了。3、运行以前步骤都正确执行,代表容器running。

2. 生命周期全流程

雷神源码讲解spring的九大事件和探针
在这里插入图片描述

2. 事件触发时机

1. 各种回调监听器

一旦看到某个源码的方法,看到 xxxxgetSpringFactoriesxxxxxx(),就知道是读spring.factories 文件的。

  • BootstrapRegistryInitializer感知特定阶段:感知 引导初始化
    • META-INF/spring.factories
    • run方法启动第一步(严格第一步其实是记录时间戳)就创建引导上下文,创建引导上下文bootstrapContext的时候触发。
    • application.addBootstrapRegistryInitializer();
    • 应用场景:项目启动之初可以利用它,进行密钥校对授权
  • ApplicationContextInitializer感知特定阶段: 感知ioc容器初始化
    • META-INF/spring.factories
    • application.addInitializers();
  • ApplicationListener: 感知全阶段:基于事件机制,感知事件(onEvent()方法)。 一旦到了哪个阶段可以做别的事
    • @Bean@EventListener事件驱动

    • SpringApplication.addListeners(…)SpringApplicationBuilder.listeners(…)

    • 在这里插入图片描述

    • META-INF/spring.factories

  • SpringApplicationRunListener: 感知全阶段生命周期 + 各种阶段都能自定义操作; 功能更完善。
    • META-INF/spring.factories
  • ApplicationRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪
    • @Bean
  • CommandLineRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪
    • @Bean

Runner怎么使用(根据源码:需要放入ioc容器)
在这里插入图片描述
在这里插入图片描述

最佳实战:

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

2. 完整触发流程

9大事件触发顺序&时机

  1. ApplicationStartingEvent:应用启动但未做任何事情, 除过注册listeners and initializers.
  2. ApplicationEnvironmentPreparedEvent: Environment 准备好,但context 未创建.
  3. ApplicationContextInitializedEvent: ApplicationContext 准备好,ApplicationContextInitializers 调用,但是任何bean未加载
  4. ApplicationPreparedEvent: 容器刷新之前,bean定义信息加载
  5. ApplicationStartedEvent: 容器刷新完成, runner未调用
    =以下就开始插入了探针机制====
  6. AvailabilityChangeEventLivenessState.CORRECT应用存活; 存活探针
  7. ApplicationReadyEvent: 任何runner被调用
  8. AvailabilityChangeEventReadinessState.ACCEPTING_TRAFFIC 就绪探针,可以接请求
  9. ApplicationFailedEvent :启动出错
    在这里插入图片描述
    应用事件发送顺序如下:
    在这里插入图片描述

感知应用是否存活了:可能植物状态,虽然活着但是不能处理请求。
应用是否就绪了:能响应请求,说明确实活的比较好。

3. SpringBoot 事件驱动开发

springboot事件驱动开发讲解

应用启动过程生命周期事件感知(9大事件)、应用运行中事件感知(无数种)。

  • 事件发布ApplicationEventPublisherAware注入:ApplicationEventMulticaster
  • 事件监听组件 + @EventListener

在这里插入图片描述
在这里插入图片描述
之前只能通过Controller类中注入数个Service然后利用他们的方法进行功能实现
在这里插入图片描述

事件发布者

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

事件订阅者

实现接口的写法——> 更推荐下面的使用自定义方法和注解的方法

@Service
public class StealService implements ApplicationListener<LoginSuccessEvent> {public void steal(UserEntity userEntity){System.out.println("-------盗号成功---------");System.out.println(userEntity.getUserName() + ":" + userEntity.getPassword());}@Overridepublic void onApplicationEvent(LoginSuccessEvent event) {System.out.println("StealService=====  收到事件  =======");UserEntity userEntity = (UserEntity) event.getSource();steal(userEntity);}
}

使用自定义方法和注解的方法

@Service
public class CouponService {@Order(1)@EventListenerpublic void onEvent(LoginSuccessEvent loginSuccessEvent){System.out.println("===== CouponService ====感知到事件"+loginSuccessEvent);UserEntity source = (UserEntity) loginSuccessEvent.getSource();sendCoupon(source.getUsername());}public void sendCoupon(String username){System.out.println(username + " 随机得到了一张优惠券");}
}

2. 自动配置原理

1. 入门理解

应用关注的三大核心:场景、配置、组件

1. 自动配置流程

在这里插入图片描述

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

2. SPI机制

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

在SpringBoot中,META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

作业:写一段java的spi机制代码

3. 功能开关

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

2. 进阶理解

1. @SpringBootApplication

@SpringBootConfiguration

就是: @Configuration ,容器中的组件,配置类。spring ioc启动就会加载创建这个类对象

@EnableAutoConfiguration:开启自动配置

开启自动配置 具体细节分析——>第二节

@AutoConfigurationPackage:扫描主程序包:加载自己的组件

  • 利用 @Import(AutoConfigurationPackages.Registrar.class) 想要给容器中导入组件(获取主程序的信息,然后通过注册方法批量注册)。
  • 把主程序所在的的所有组件导入进来。
  • 为什么SpringBoot默认只扫描主程序所在的包及其子包

@Import(AutoConfigurationImportSelector.class):加载所有自动配置类:加载starter导入的组件

		List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).getCandidates();

扫描SPI文件:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@ComponentScan

组件扫描:排除一些组件(哪些不要)
排除前面已经扫描进来的配置类、和自动配置类

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

2. 完整启动加载流程

生命周期启动加载流程
在这里插入图片描述

3. 自定义starter

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

    1. 创建自定义starter项目,引入spring-boot-starter基础依赖
    1. 编写模块功能,引入模块所有需要的依赖。
    1. 编写xxxAutoConfiguration自动配置类,帮其他项目导入这个模块需要的所有组件
    1. 编写配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports指定启动需要加载的自动配置
    1. 其他项目引入即可使用

1. 业务代码

自定义配置有提示。导入以下依赖重启项目,再写配置文件就有提示

@ConfigurationProperties(prefix = "robot")  //此属性类和配置文件指定前缀绑定
@Component
@Data
public class RobotProperties {private String name;private String age;private String email;
}
<!--        导入配置处理器,配置文件自定义的properties配置都会有提示--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>

2. 基本抽取

  • 创建starter项目,把公共代码需要的所有依赖导入
  • 把公共代码复制进来
  • 自己写一个 RobotAutoConfiguration,给容器中导入这个场景需要的所有组件
    • 为什么这些组件默认不会扫描进去?
    • starter所在的包和 引入它的项目的主程序所在的包不是父子层级
  • 别人引用这个starter,直接导入这个 RobotAutoConfiguration,就能把这个场景的组件导入进来
  • 功能生效。
  • 测试编写配置文件

3. 使用@EnableXxx机制

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

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

4. 完全自动配置

  • 依赖SpringBoot的SPI机制
  • META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中编写好我们自动配置类的全类名即可
  • 项目启动,自动加载我们的自动配置类

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开发必备,开源 or 免费的 AI 编程助手

AI 大模型的火热&#xff0c;让开发圈近来如虎添翼&#xff0c;各种各样基于 AI 技术的开发者工具和新范式不断涌现&#xff0c;尤其是 Github 和 OpenAI 共同推出的 Copilot X &#xff0c;更是一骑绝尘。本文推荐一些开源 or 免费的 AI 编程工具&#xff0c;不妨试着用起来。…

超过5000人的2年研究表明,这一活动破坏你的身心健康

Tips 原文作者&#xff1a;Minda Zetlin 原文出处&#xff1a;A 2-Year Study of More Than 5,000 People Shows This 1 Activity Destroys Your Emotional and Physical Health 阅读时&#xff0c;把文中的 Fackbook 换成微信。 国外主要用 Facebook&#xff1b; 国内主要是微…

申请阿里云服务器并搭建公网可支持数据上传下载的HTTP服务器

1. 前言 拥有一台自己的云服务器可以做很多事情。阿里云服务器毫无疑问是国内最好的。 阿里云服务器可以用于各种互联网应用的搭建和运行&#xff0c;提供稳定、高性能的服务。 阿里云服务器的用途&#xff0c;包括但不限于以下几个方面&#xff1a; 网站托管&#xff1a;可以将…

谷歌眼镜秀出时尚风采:对面的女孩看过来

摘要&#xff1a;在近日举办的纽约时尚周上&#xff0c;让身材火辣的模特带上谷歌的眼镜&#xff0c;行走在T台之上。主打时尚牌&#xff0c;进一步加固谷歌眼镜在大众消费阶层的印象&#xff0c;尤其是女性消费者。谷歌眼镜创始人Sebastian Thrun指出&#xff1a;谷歌眼镜特别…

学生台灯什么牌子好对眼睛好?专业护眼灯的学生台灯分享

据报告统计&#xff0c;2022年我国儿童青少年总体近视率为52.7%&#xff0c;其中6岁儿童为14.3%&#xff0c;小学生为35.6%&#xff0c;初中生为71.1%&#xff0c;高中生为80.5%&#xff0c;这些数据让人不寒而栗&#xff01; 专家表示&#xff0c;导致儿童青少年近视的因素&am…

【UGP VR眼镜排行榜】2018VR眼镜眼镜哪个好?什么VR眼镜值得买?综合推荐十大热品

科技的发展&#xff0c;高科技产品层出不穷&#xff0c;VR眼镜的出现使人们足不出户也能享受到高品质的观影感受。VR(Virtual Reality&#xff09;即虚拟现实&#xff0c;简称VR.虚拟现实头戴显示器设备&#xff0c;简称VR头显VR眼镜.现在&#xff0c;VR眼镜已不是什么稀奇的东…

《谷歌眼镜》新书作者:眼镜需要成为AR的载体吗?

近10年前&#xff0c;谷歌推出了首款AR眼镜Google Glass&#xff0c;尽管这款产品并没有如预期般取得成功&#xff0c;但它为后续AR硬件技术的发展奠定了基础。我们知道&#xff0c;从微软HoloLens开始&#xff0c;AR头显/眼镜产品更侧重于B端应用&#xff0c;面向C端发售的很少…

Karl Guttag:现有Micro LED/LCoS+光波导AR眼镜对比解析

轻量化是未来AR眼镜的发展趋势&#xff0c;为了缩减尺寸&#xff0c;AR眼镜厂商尝试了多种方案&#xff0c;长期来看Micro LED光机在小型化上更有优势&#xff0c;但现阶段LCoS光机的图像表现更好。在CES 2023期间&#xff0c;DigiLens、Lumus、Vuzix、OPPO、Avegant也展出了不…

偏光太阳镜测试图片软件,[专题]真假偏光太阳镜简单、实用辨别方法!

偏光太阳镜主要是通过镜片的平衡排列的结晶体原理&#xff0c;只让与晶体平衡的光波通过&#xff0c;而向其它角度震动的光波会一律被阻挡的方法(如同百叶窗的原理)制作而成。 正是利用这种原理&#xff0c;偏光太阳镜便可以有效地排除和滤除光束中的偏振光&#xff0c;使光线能…

智能眼镜的两种显示方式

to管理员&#xff1a;哪一个是广告&#xff0c;全是广告&#xff01;难不成网友的链接都不能给了&#xff1f;&#xff01;你们的评判标识是什么&#xff1f; 就现有的技术而言&#xff0c;受限于通讯及周边模块、电源的限制&#xff0c;眼镜只适合于作为显示器使用。 眼镜显示…

谷歌眼镜

谷歌眼镜(Google Project Glass)是由谷歌公司于2012年4月发布的一款“拓展现实”眼镜&#xff0c;它具有和智能手机一样的功能&#xff0c;可以通过声音控制拍照&#xff0c;视频通话和辨明方向以及上网冲浪、处理文字信息和电子邮件等。 查看精彩图册 目录 产品简介 发布信息…

微信小程序开发制作 | 小程序开发者工具功能介绍

小程序开发者工具是微信官方提供的用于开发和调试小程序的工具。它支持 Windows 和 Mac 两种操作系统&#xff0c;并提供了许多实用的功能&#xff0c;使得小程序开发者能够快速地开发和调试小程序。 下面是小程序开发者工具的主要功能介绍&#xff1a; 1.编辑器&#xff1a;…

微信里的小程序怎么制作

自小程序普及以来&#xff0c;除了公司企业&#xff0c;很多的个体户商家都会想了解微信里的小程序怎么制作的&#xff0c;毕竟小程序能解决很多经营上的需求。那么就给大家讲解微信里的小程序怎么制作的流程&#xff0c;希望大家对此能有了解。 流程一、制作小程序前准备 我…

微信小程序开发之——制作表格

一 概述 表格样式一表格样式二 二 绘制过程 外层设置display:table&#xff0c;并设置border-collapse表格边框模型表头设置display:table-row&#xff0c;单元格设置为display:table-cell每一行单元格同表头设置 三 示例代码 3.1 table.wxml(布局文件) <view class&qu…

发明了万维网的他,如今却想亲手推翻它

本文转载自 差评 他有个计划&#xff0c;一个推翻现有互联网&#xff0c;重建数字世界的计划。 看到这句话时&#xff0c;相信很多差友和差评君的第一反应一个样&#xff1a;这谁也太狂了吧&#xff1f;几个菜啊&#xff0c;喝成这样&#xff1f; 毕竟&#xff0c;就算是马云…

计算机中文核心投递经历

中文核心投递录用经历 这篇文章主要记录了我在研究生期间从论文投递到录用的一个心酸过程&#xff0c;因为文章质量不是很高&#xff0c;所以投递过程中也是多次被拒稿。接下来介绍一下我的经历&#xff0c;为后来人提供一些经验。 这篇文章我是从2021年9月份开始着手写&#…

计算机专业留学动机信范文,出国留学,如何写好动机信(Motivation Letter)?

一篇好的动机信最重要的是简洁易懂,用最简洁的语言展示申请者最突出的优点。 浙大毕业后在美国(UIUC)和欧洲(KTH, CTH, EPFL, NTNU)留学,PhD。另外由于在之前的工作中也参与系里招生,帮老板评审申请材料,参与系里招生会议。经手的材料主要有以下几种:需要做论文的硕士,…

易水寒 服务器 位置,她彻底在他所在的城市消失了,他开始有些烦躁

封笑笑死了&#xff0c;她彻底在易水寒所在的城市消失了&#xff0c;连同名字身份都成了再也不会属于这个世界的东西。 易水寒开始有些烦躁&#xff0c;罗云若和罗家那些有意无意的提示让他觉得自己的自由和思维正在被束缚&#xff0c;这种感觉是他最厌恶的。 而罗云若似乎也变…

云计算进入全新时代(1)-ielab

一、什么是混合云&#xff1a; 混合云融合了公有云和私有云&#xff0c;是近年来云计算的主要模式和发展方向。我们已经知道私有云主要是面向企业用户&#xff0c;出于安全考虑&#xff0c;企业更愿意将数据存放在私有云中&#xff0c;但是同时又希望可以获得公有云的计算资源…

秋天的第一杯奶茶刷屏互联网,其背后又蕴含着哪些营销逻辑呢?

8 月 7 日&#xff0c;是我国的传统节气——立秋&#xff0c;立秋是凉爽的秋季的开始&#xff0c;在这一节气过后&#xff0c;天气会逐渐变得凉爽。立秋的到来也预示着我们正式从夏季迈入秋季。 在立秋这一天&#xff0c;秋天的第一杯奶茶这个话题可以说是横扫网络&#xff0c…