从零玩转系列之SpringBoot3-核心原理

news2024/12/23 8:09:50

一、简介

1.前置知识

● Java17
● Spring、SpringMVC、MyBatis
● Maven、IDEA

2.环境要求

环境&工具版本(or later)
SpringBoot3.1.x
IDEA2023.x
Java17+
Maven3.5+
Tomcat10.0+
Servlet5.0+
GraalVM Community22.3+
Native Build Tools0.9.19+

二、SpringBoot3-核心原理

1.事件和监听器

1.1. 生命周期监听

1.2. 监听器-SpringApplicationRunListener

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

输入图片说明

输入图片说明

2. 生命周期

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

3.事件触发时机

1. 各种回调监听器

  • BootstrapRegistryInitializer感知特定阶段:感知引导初始化

    • META-INF/spring.factories
    • 创建引导上下文bootstrapContext的时候触发。
    • application.addBootstrapRegistryInitializer();
    • 场景:进行密钥校对授权。
  • ApplicationContextInitializer: 感知特定阶段: 感知ioc容器初始化

    • META-INF/spring.factories
    • application.addInitializers();
  • ApplicationListener: 感知全阶段:基于事件机制,感知事件。 一旦到了哪个阶段可以做别的事

    • @Bean@EventListener事件驱动
    • SpringApplication.addListeners(…)SpringApplicationBuilder.listeners(…)
    • META-INF/spring.factories
  • SpringApplicationRunListener: 感知全阶段生命周期 + 各种阶段都能自定义操作; 功能更完善。

    • META-INF/spring.factories
  • ApplicationRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪

    • @Bean
  • CommandLineRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪

    • @Bean

最佳实战:

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

2. 完整触发流程

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 :启动出错

生命周期

应用事件发送顺序如下:

输入图片说明

感知应用是否存活了:可能植物状态,虽然活着但是不能处理请求。

应用是否就绪了:能响应请求,说明确实活的比较好。

3. SpringBoot 事件驱动开发

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

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

输入图片说明

输入图片说明

事件发布者

@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
     */
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

事件订阅者

@Service
public class CouponService {

    @Order(1)
    @EventListener
    public 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 + " 随机得到了一张优惠券");
    }
}

3. 自动配置原理

1. 入门理解

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

1. 自动配置流程

输入图片说明

  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配置好的组件随时使用

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基础依赖
  • 2.编写模块功能,引入模块所有需要的依赖。
  • 3.编写xxxAutoConfiguration自动配置类,帮其他项目导入这个模块需要的所有组件
  • 4.编写配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports指定启动需要加载的自动配置
  • 5.其他项目引入即可使用

1. 业务代码

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

@ConfigurationProperties(prefix = "robot")  //此属性类和配置文件指定前缀绑定
@Component
@Data
public class RobotProperties {

    private String name;
    private String age;
    private String email;
}
@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><!--        导入配置处理器,配置文件自定义的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 {


}@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.coloradmin.cn/o/758777.html

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

相关文章

【Hippo4j监控Web容器Tomcat线程池】

&#x1f680; 线程池管理工具-Hippo4j &#x1f680; &#x1f332; AI工具、AI绘图、AI专栏 &#x1f340; &#x1f332; 如果你想学到最前沿、最火爆的技术&#xff0c;赶快加入吧✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域优质创作者&#…

知识整合:Web页面请求的历程

Web页面请求的历程 内部涉及知识&#xff1a;一、准备:DHCP、UDP、IP 和以太网二、仍在准备&#xff1a;DNS和ARP三、仍在准备&#xff1a;域内路由选择到DNS服务器四、Web客户-服务器交互&#xff1a;TCP和HTTP五、HTTP请求响应格式Requests部分Responses 部分 下载一个Web页面…

2023.7.15

同余最短路 P3403 跳楼机 题意&#xff1a;给定h高的楼层&#xff0c;起始位置在第一层&#xff0c;可以选择操作向上移动x层或y层或z层&#xff0c;回到第一层 求可以到达的楼层数 思路&#xff1a;转化题意为求axbyczk(k在[1,h]&#xff0c;x,y,z为正整数,有多少k满足条件&am…

基础IO

1.C的文件接口 "r" - 只读模式&#xff0c;打开文件用于读取&#xff0c;文件必须存在。 "w" - 写模式&#xff0c;打开文件用于写入&#xff0c;如果文件已存在则清空文件内容&#xff0c;如果文件不存在则创建新文件。 "a" - 追加模式&#…

手把手搭建mybatis入门程序

目录 准备数据库表 搭建工程 引入日志框架lockback SqlSessionUtil工具类封装 准备数据库表 CREATE TABLE t_car (id bigint NOT NULL AUTO_INCREMENT COMMENT 主键,car_num varchar(100) DEFAULT NULL COMMENT 汽车编号,brand varchar(100) DEFAULT NULL COMMENT 品牌,gui…

阿里云2核4G服务器能搭建几个网站?性能如何?

2核4G服务器能安装多少个网站&#xff1f;2核4g配置能承载多少个网站&#xff1f;一台2核4G服务器可以安装多少个网站&#xff1f;阿腾云2核4G5M带宽服务器目前安装了14个网站&#xff0c;从技术角度是没有限制的&#xff0c;只要云服务器性能够用&#xff0c;想安装几个网站就…

Java正则表达式校验某个字符串是否是合格的email

Java正则表达式校验某个字符串是否是合格的email 可以借助正则表达式校验某个字符串是否是合规的电子邮箱。对于邮箱的正则表达式有严格的模式&#xff0c;如&#xff1a;^[a-zA-Z0-9_&*-](?:\\.[a-zA-Z0-9_&*-])*(?:[a-zA-Z0-9-]\\.)[a-zA-Z]{2,7}$ 对应的Java实现…

Verilog基础之十六、RAM实现

目录 一、前言 二、工程设计 2.1 RAM IP核使用 2.2 设计代码 2.3 仿真代码 2.4 综合结果 2.5 仿真结果 一、前言 工程设计中除逻辑计算单元外&#xff0c;存储单元也是不可获取的部分&#xff0c;RAM(Random Access Memory)随机存取存储器即可以写入数据&#xff0c;也可…

Spring Cloud Gateway下的GC停顿排查之旅

01 背景 在微服务架构体系流行的当下&#xff0c;Spring Cloud全家桶已经是大多数团队的首选&#xff0c;我们也不例外&#xff0c;并且选择了Spring Cloud Gateway作为了业务网关&#xff0c;进行了一些通用能力的开发&#xff0c;如鉴权、路由等等。作为一个成熟的框架&#…

非线性弹簧摆的仿真(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

matplotlib定制绘图的线型、标记类型

文章目录 折线图参数列表实战演示特征字符串 折线图 折线图是科研绘图中最常见的一种图形&#xff0c; 表现的是数据的变化情况 import numpy as np import matplotlib.pyplot as pltx np.linspace(0,10,100) y np.sin(x) plt.plot(x,y) plt.show()参数列表 尽管绘图逻辑十…

Tensorflow-gpu保姆级安装教程(Win11, Anaconda3,Python3.9)

Tensorflow-gpu 保姆级安装教程&#xff08;Win11, Anaconda3&#xff0c;Python3.9&#xff09; 前言Tensorflow-gpu版本安装的准备工作(一)、查看电脑的显卡&#xff1a;(二) 、Anaconda的安装(三)、cuda下载和安装(四)、cudnn下载安装(五)、配置环境变量(六)、创建 tensorfl…

idea生成类关系图Diagrams

打开或选择该类&#xff0c;此处我以FutureTask类为例 在打开的该类内容内随意地方右键&#xff08;或选中该类文件&#xff09;》选择Diagrams》Show Diagrams》Java Classes此时就可以看到该类的关系图了

剖析C语言字符串函数

目录 前言&#xff1a; 一、strlen函数 功能&#xff1a; 参数和返回值&#xff1a; 注意事项&#xff1a; 返回值是无符号的易错点&#xff1a; strlen函数的模拟实现 1、计数器算法 2、递归算法 3、指针减去指针 二、strcpy函数 功能&#xff1a; 参数和返回值 …

【小沐学NLP】龙猫-InsCode Stable Diffusion 美图活动一期

文章目录 1、应用简介1.1 Stable Diffusion模型1.2 Stable Diffusion WebUI1.3 InsCode平台 2、界面简介2.1 打开Stable Diffusion WebUI2.2 选择模型2.3 选择功能模块2.4 文本输入2.5 参数设置 3、测试3.1 龙猫3.2 恐龙3.3 蓝天白云3.4 美少女战士 4、其他4.1 DALLE 24.2 Drea…

Linux调试工具GDB(2)

文章目录 前言一、数据断点二、x查看内存命令三、深入info命令四、调试中的技巧总结 前言 本篇文章我们继续讲解GDB调试。 一、数据断点 在 GDB (GNU Debugger) 中&#xff0c;数据断点是一种断点类型&#xff0c;它允许你在程序访问指定内存地址的数据时暂停程序的执行。通…

浅聊 【ThreadLocal】(超级详细)

写在开始 : 本文主要讲述 : ThreadLocal简介; 常用API; demo案例; 特点引用场景;以及部分底层原理源码内容。 引言 &#xff1a; 从常见面试题看 ThreadLocal: **①解释 **&#xff1a; ThreadLocal是多线程中对于解决线程安全的一个操作类&#xff0c;它会为每个线程都分 配一…

121、仿真-基于51单片机8路温度 ds18b20多路温度传感器检测仿真设计(Proteus仿真+程序+原理图+参考论文+任务书+流程图等)

摘 要 随着社会经济的高速发展和科技水平的不断进步&#xff0c;温度监控器的运用范围越来越广泛&#xff0c;也渐渐地发展到了核变站的温度监控。温度与人们的生活生产密切相关&#xff0c;比如在核变站的环境下&#xff0c;对温度的监控更是必不可少的&#xff0c;不但能保…

【代码随想录 | Leetcode | 第四天】数组 | 螺旋矩阵 | 59-54

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来螺旋矩阵的分享✨ 目录 前言59. 螺旋矩阵 II54. 螺旋矩阵总结 59. 螺旋矩阵 II 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n…

ASIDE-Simulink接口预研报告

ASIDE-Simulink接口预研报告 2018年4月 1 引言 本报告描述ASIDE-Simulink接口的预先研究情况和结果。 2 目的 ASIDE-Simulink接口是ASIDE中的一个模块。它的主要功能是通过解析Simulink模型文件&#xff0c;把Simulink模型中的相关信息导入到ASIDE中&#xff0c;从而把Simu…