1. 事件和監聽器
1. 生命周期監聽
場景:監聽應用的生命周期
1. 監聽器-SpringApplicationRunListener
- 自定義
SpringApplicationRunListener
來監聽事件;
-
- 編寫
SpringApplicationRunListener
實現類 - 在
META-INF/spring.factories
中配置org.springframework.boot.SpringApplicationRunListener=自己的Listener
,還可以指定一個有參構造器,接受兩個參數(SpringApplication application, String[] args)
- springboot 在
spring-boot.jar
中配置了默認的 Listener,如下
- 編寫
/*** 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。*/
2. 生命周期全流程
2. 事件觸發時機
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*/@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}
}
事件訂閱者
@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. 自動配置流程
- 導入
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;
}
<!-- 導入配置處理器,配置文件自定義的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 文件中編寫好我們自動配置類的全類名即可
- 項目啟動,自動加載我們的自動配置類