日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot事件与监听机制

發布時間:2024/10/5 javascript 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot事件与监听机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景:最近需要在項目啟動前去做初始化腳本,所以看了一下關于springboot的監聽機制,做一下記錄

通常我們啟動應用就使用這么一條命令SpringApplication.run(XXXX.class,args);然后我們的項目就啟動了。是不是早就想知道run之后發生了什么?

我們跟蹤進去,就會來到下圖的run方法。

從圖中代碼可知:

  • 構造SpringApplication對象
  • 調用該對象的run方法
  • 該對象的run方法返回了實現ConfigurableApplicationContext接口的對象
  • 與我們主題相關的內容在run方法里。

    這里面有兩個內容,第一初始化SpringApplication,第二是初始化SpringApplication后開始真正運行run方法

    ?

    先看SpringApplication的初始化,其實里面最重要的就是SpringBoot自動裝載機制,像監聽器Listeners初始化,還有什么InitiaLizers。當然還有很多其他初始化任務。這里我們來重點看一下Listener監聽注冊的初始化。

    ? 這里我們重點看下setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));,由getSpringFactoriesInstances進去可以看到

    ?這里的names就是收集了初始化后的ApplicationListener的所有實現的監聽器,具體有10個如下圖:

    ?接下來給大家再往下具體分析這個監聽器是如何被初始化的,進入SpringFactoriesLoader.loadFactoryNames(type, classLoader)中如下

    然后再進入loadSpringFactories方法,如下:

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {//這里將會最終存放所有初始化后的監聽器MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?//下面的的 FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”,這個地方往下就會涉及到Spring的自動裝配SPI機制了classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryClassName = ((String) entry.getKey()).trim();for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryClassName, factoryName.trim());}}} //這里將會吧所有的初始化的監聽對象放入緩存中cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}

    那么上面的spring.factories文件是在哪里的呢,通過調試得知如下圖

    由此可知我們可以按照這個path找出這個文件

    ? ?現在讓我們來看下這個文件到底張什么樣的,我們打開這個文章一起來看下

    # PropertySource Loadersorg.springframework.boot.env.PropertySourceLoader=\org.springframework.boot.env.PropertiesPropertySourceLoader,\org.springframework.boot.env.YamlPropertySourceLoader# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\org.springframework.boot.context.event.EventPublishingRunListener# Error Reportersorg.springframework.boot.SpringBootExceptionReporter=\org.springframework.boot.diagnostics.FailureAnalyzers# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\org.springframework.boot.context.ContextIdApplicationContextInitializer,\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer# Application Listenersorg.springframework.context.ApplicationListener=\ //以下都是ApplicatinListener的實現類,這些類都將會在上面org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\org.springframework.boot.context.FileEncodingApplicationListener,\org.springframework.boot.context.config.AnsiOutputApplicationListener,\org.springframework.boot.context.config.ConfigFileApplicationListener,\org.springframework.boot.context.config.DelegatingApplicationListener,\org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.context.logging.LoggingApplicationListener,\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener# Environment Post Processorsorg.springframework.boot.env.EnvironmentPostProcessor=\org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor# Failure Analyzersorg.springframework.boot.diagnostics.FailureAnalyzer=\org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer# FailureAnalysisReportersorg.springframework.boot.diagnostics.FailureAnalysisReporter=\org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

    這里我們來具體看下ApplictionListener加載的那些類,因為這些跟我們接下來要講的CnfigFileListener會有關系。其實很簡單,在MultiValueMap<String, String> result = cache.get(classLoader);的結果集別刻意看出,因為最終初始化的所有對象都將add到這個result中去。看下結果圖如下:

    那么有了以上的基礎我們可以自己定義一些事件,讓容器在初始化spring容器之前為我們做些事情:

    我們嘗試自己創建實現ApplictionListener:

    import com.mgk.demov1.annotation.Student; import com.sun.org.apache.bcel.internal.generic.SWITCH; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.*; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextStartedEvent; import org.springframework.context.event.ContextStoppedEvent;public class MyListenery implements ApplicationListener {@Overridepublic void onApplicationEvent(ApplicationEvent event) { // ApplicationStartingEvent//啟動開始的時候執行的事件 // ApplicationEnvironmentPreparedEvent//上下文創建之前運行的事件 // ApplicationContextInitializedEvent// // ApplicationPreparedEvent//上下文創建完成,注入的bean還沒加載完成 // ContextRefreshedEvent//上下文刷新 // ServletWebServerInitializedEvent//web服務器初始化 // ApplicationStartedEvent// // ApplicationReadyEvent//啟動成功 // ApplicationFailedEvent//在啟動Spring發生異常時觸發switch (event.getClass().getSimpleName()){case "ApplicationStartingEvent":System.out.println("啟動開始的時候執行的事件");break;case "ApplicationEnvironmentPreparedEvent":System.out.println("上下文創建之前運行的事件");break;case "ApplicationContextInitializedEvent":System.out.println("上下文初始化");break;case "ApplicationPreparedEvent":System.out.println("上下文創建完成,注入的bean還沒加載完成");break;case "ContextRefreshedEvent":System.out.println("上下文刷新");if( event instanceof ContextRefreshedEvent){Object stu = ((ContextRefreshedEvent) event).getApplicationContext().getBean("stu");System.out.println(stu);}break;case "ApplicationStartedEvent":System.out.println("ApplicationStartedEvent");break;case "ApplicationReadyEvent":System.out.println("啟動成功");break;case "ApplicationFailedEvent":break;}} }

    也可以單獨去實現其中一個監聽事件,來解決業務具體操作

    import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent;public class MyListenerv2 implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {System.out.println(contextRefreshedEvent);}

    主程序這邊可以添加多個自定義監聽順序自上而下

    SpringApplication app = new SpringApplication(Demov1Application.class);app.addListeners(new MyListenery());app.addListeners(new MyListenerv2());app.run(args);

    springboot支持的事件類型如下:

    • ApplicationFailedEvent:該事件在springboot啟動失敗是調用
    • ApplicationPreparedEvent:上下文context準備時觸發
    • ApplicationReadyEvent:上下文已經準備完畢的時候觸發
    • ApplicationStartedEvent:spring boot 啟動監聽類
    • SpringApplicationEvent:獲取SpringApplication
    • ApplicationEnvironmentPreparedEvent:環境事先準備

    哪些場景會用到

    1.啟動前環境檢測?

    2.啟動時配置初始化?

    3.啟動后數據初始化?

    ...

    應用的場景很多,可以發揮我們的想象。

    我們還看到springboot啟動是還有很多其它很多bean也可以實現類是的監聽功能,在事件發生的時候

    事件回調機制

    代碼樣例

    分別對4個類繼承

    HelloApplicationContextInitializer

    HelloApplicationRunner @Component public class HelloApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("運行ApplicationRunner:ApplicationRunner...run....");} }

    ?HelloCommandLineRunner類

    @Component public class HelloCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("運行LineRunner:CommandLineRunner...run..."+ Arrays.asList(args));} } HelloSpringApplicationRunListener類 package com.limp.listerner;import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplicationRunListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment;public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {//必須有的構造器public HelloSpringApplicationRunListener(SpringApplication application, String[] args){}@Overridepublic void starting() {System.out.println("運行RunListener:SpringApplicationRunListener...starting...");}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {Object o = environment.getSystemProperties().get("os.name");System.out.println("獲取系統環境:SpringApplicationRunListener...environmentPrepared.."+o);}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("SpringApplicationRunListener...contextPrepared...");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("SpringApplicationRunListener...contextLoaded...");}@Overridepublic void started(ConfigurableApplicationContext context) {}@Overridepublic void running(ConfigurableApplicationContext context) {}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {}}

    注意上面2個類需要META-INF/spring.factories添加如下配置才能生效

    org.springframework.context.ApplicationContextInitializer=\ com.limp.listerner.HelloApplicationContextInitializerorg.springframework.boot.SpringApplicationRunListener=\ com.limp.listerner.HelloSpringApplicationRunListene

    開始測試

    啟動應用...運行結果如下

    運行RunListener:SpringApplicationRunListener...starting... 獲取系統環境:SpringApplicationRunListener...environmentPrepared..Windows 10 ..... 運行Initializer:ApplicationContextInitializer...initialize...org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@557cd14f: startup date [Thu Jan 01 08:00:00 CST 1970]; root of context hierarchy運行ApplicationRunner:ApplicationRunner...run.... 運行LineRunner:CommandLineRunner...run...[]

    參考文獻:

    SpringBoot啟動及配置文件加載原理分析:?https://www.cnblogs.com/dszazhy/p/11513012.html

    如何自定義啟動監聽:https://blog.csdn.net/zzhuan_1/article/details/85312053

    原理是什么?https://www.cnblogs.com/dszazhy/p/11513012.html

    ?

    總結

    以上是生活随笔為你收集整理的SpringBoot事件与监听机制的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。