一、简介

1.前置知识

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

2.环境要求

环境&工具 版本(or later)
SpringBoot 3.1.x
IDEA 2023.x
Java 17+
Maven 3.5+
Tomcat 10.0+
Servlet 5.0+
GraalVM Community 22.3+
Native Build Tools 0.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 文件中编写好我们自动配置类的全类名即可
  • 项目启动,自动加载我们的自动配置类

从零玩转系列之SpringBoot3-核心原理的更多相关文章

  1. 《从零玩转JavaWeb+项目实战》-系列课堂录制计划

    点击试听课程 前言 很多自学编程的同学经常和我说想学一门语言自己到网上找一些教程看到一半就像背单词背到ambulance一样坚持不下去了....究其原因基本上都是:内容太多,太枯燥,专业术语听不懂,学 ...

  2. 【JAVA零基础入门系列】Day6 Java字符串

    字符串,是我们最常用的类型,每个用双引号来表示的串都是一个字符串.Java中的字符串是一个预定义的类,跟C++ 一样叫String,而不是Char数组.至于什么叫做类,暂时不做过多介绍,在之后的篇章中 ...

  3. 【JAVA零基础入门系列】Day12 Java类的简单应用

    俗话说的好,实践出真知,所以除了理论知识掌握扎实以外,更重要的是要多加操练,这样才能掌握核心科技. 今天我们就用刚学会的类来实践一下,目标便是完成上一篇中的剁手任务. 我们的商品类已经准备好了,代码重 ...

  4. hadoop系列:zookeeper(2)——zookeeper核心原理(选举)

    1.前述 上篇文章<hadoop系列:zookeeper(1)--zookeeper单点和集群安装>(http://blog.csdn.net/yinwenjie/article/deta ...

  5. AB实验的高端玩法系列3 - AB组不随机?观测试验?Propensity Score

    背景 都说随机是AB实验的核心,为什么随机这么重要呢?有人说因为随机所以AB组整体不存在差异,这样才能准确估计实验效果(ATE) \[ ATE = E(Y_t(1) - Y_c(0)) \] 那究竟随 ...

  6. Word 查找替换高级玩法系列之 -- 把论文中的缩写词快速变成目录下边的注释表

    1. 前言 问题:Word写论文如何把文中的缩写快速转换成注释表? 原来样子: 想要的样子: 2. 步骤 使用查找替换高级用法,替换缩写顺序 选中所有文字 打开查找替换对话框,输入以下表达式: 替换后 ...

  7. 【用PS3手柄在安卓设备上玩游戏系列】连接手柄和设备

    背景 硬件要求1:PS3 手柄 + 手柄配套的USB线 硬件要求2:已经获得 ROOT 权限并且支持蓝牙的安卓设备 软件要求1:Sixaxis Compatibility Checker PS3 手柄 ...

  8. 【JAVA零基础入门系列】Day2 Java集成开发环境IDEA

    开发环境搭建好之后,还需要一个集成开发环境也就是IDE来进行编程.这里推荐的IDE是IDEA,那个老掉牙的Eclipse还是先放一边吧,(手动滑稽). IDEA的下载地址:http://www.jet ...

  9. 【JAVA零基础入门系列】Day4 变量与常量

    这一篇主要讲解Java中的变量,什么是变量,变量的作用以及如何声明,使用变量. 那么什么是变量?对于初学者而言,可以将变量理解为盒子,这些盒子可以用来存放数据,不同类型的数据需要放在对应类型的盒子里. ...

  10. 【JAVA零基础入门系列】Day5 Java中的运算符

    运算符,顾名思义就是用于运算的符号,比如最简单的+-*/,这些运算符可以用来进行数学运算,举个最简单的栗子: 已知长方形的长为3cm,高为4cm,求长方形的面积. 好,我们先新建一个项目,命名为Rec ...

随机推荐

  1. C++模板(函数模板 & 类模板)

    模板编程可称范型编程,是一种忽视数据类型的编程方式,这样的好处是什么?且看下面一个例子: 简单使用 求解最值问题,返回两个值中的较大值: int Max(int a, int b) { return ...

  2. React中图片的相对路径引入和绝对路径引入

    React中当在JSX中的img标签中引入时使用相对路径引入,地址是基于index.html的而不是当前jsx文件的,如 <img src="./src/assets/images/g ...

  3. 从零开始TP6配置ThinkPHP-ApiDoc

    系统:windows11 集成环境:小皮(原phpstudy) composer:2.5 准备工作:安装小皮后,在软件管理中安装composer,2.3安装不上去,只能安装1.8.5,没关系安装后升级 ...

  4. ASP.NET Core Web API 流式返回,逐字显示

    Websocket.SSE(Server-Sent Events)和长轮询(Long Polling)都是用于网页和服务端通信的技术. Websocket是一种全双工通信协议,能够实现客户端和服务端之 ...

  5. ChatGPT在工业领域的研究与应用探索-数据与工况认知

    1.      ChatGPT发展现状... 2 2.      ChatGPT如何与工业相结合... 2 3.      ChatGPT在工业领域的研究与应用... 3 1.   ChatGPT发展 ...

  6. 【leetcode】258. 各位相加

    [leetcode]258. 各位相加 C++解法: class Solution { public: int addDigits(int num) { string s;//用来将num转换成字符串 ...

  7. 安装anaconda遇到的一些问题

    文章目录 前言 一.报错:jupyter notebook:Bad file descriptor (C:\ci\zeromq_1602704446950\work\src\epoll.cpp:100 ...

  8. 关于java中的多态和对实例化对象的一些理解

    java面向对象三大特征即为:继承封装多态.而多态需要三大必要条件.分别是:继承.方法重写.父类引用指向子类对象.我们先一个一个来理解. 1.首先是继承和重写.这个很简单.因为多态就是建立在不同的重写 ...

  9. 2022-04-06:go中proto文件能跨平台,那是因为能生成不同语言的代码,做框架开发用protoc+插件生成代码是不合适的,需要解析,请问如何解析?

    2022-04-06:go中proto文件能跨平台,那是因为能生成不同语言的代码,做框架开发用protoc+插件生成代码是不合适的,需要解析,请问如何解析? 答案2022-04-06: go get ...

  10. 2022-04-02:你只有1*1、1*2、1*3、1*4,四种规格的砖块。 你想铺满n行m列的区域,规则如下: 1)不管那种规格的砖,都只能横着摆, 比如1*3这种规格的砖,3长度是水平

    2022-04-02:你只有11.12.13.14,四种规格的砖块. 你想铺满n行m列的区域,规则如下: 1)不管那种规格的砖,都只能横着摆, 比如1*3这种规格的砖,3长度是水平方向,1长度是竖直方 ...