# 一、簡介 ## 1.前置知識 ● Java17 ● Spring、SpringMVC、MyBatis ● Maven、IDEA ## 2.環境要求 | 環境&工具 | 版本(or later) | | : : | : : | | SpringBoot | 3.1.x | | IDEA | 202 ...
一、簡介
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
最佳實戰:
- 如果項目啟動前做事:
BootstrapRegistryInitializer
和ApplicationContextInitializer
- 如果想要在項目啟動完成後做事:
**ApplicationRunner**
和**CommandLineRunner**
- 如果要干涉生命周期做事:
**SpringApplicationRunListener**
- 如果想要用事件機制:
**ApplicationListener**
2. 完整觸發流程
9大事件
觸發順序&時機
ApplicationStartingEvent
:應用啟動但未做任何事情, 除過註冊listeners and initializers.ApplicationEnvironmentPreparedEvent
: Environment 準備好,但context 未創建.ApplicationContextInitializedEvent
: ApplicationContext 準備好,ApplicationContextInitializers 調用,但是任何bean未載入ApplicationPreparedEvent
: 容器刷新之前,bean定義信息載入ApplicationStartedEvent
: 容器刷新完成, runner未調用
=以下就開始插入了探針機制====
AvailabilityChangeEvent
:LivenessState.CORRECT
應用存活; 存活探針ApplicationReadyEvent
: 任何runner被調用AvailabilityChangeEvent
:ReadinessState.ACCEPTING_TRAFFIC
就緒探針,可以接請求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. 自動配置流程
-
導入
starter
-
依賴導入
autoconfigure
-
尋找類路徑下
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件 -
啟動,載入所有
自動配置類
xxxAutoConfiguration
-
- 給容器中配置功能
組件
組件參數
綁定到屬性類
中。xxxProperties
屬性類
和配置文件
首碼項綁定@Contional派生的條件註解
進行判斷是否組件生效
- 給容器中配置功能
-
效果:
-
- 修改配置文件,修改底層參數
- 所有場景自動配置好直接使用
- 可以註入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 文件中編寫好我們自動配置類的全類名即可
項目啟動,自動載入我們的自動配置類