banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

【轉載】六種方式,教你在SpringBoot初始化時搞點事情!

title: 【轉載】六種方式,教你在 SpringBoot 初始化時搞點事情!
date: 2021-08-24 15:19:00
comment: false
toc: true
category:

  • Java
    tags:
  • 轉載
  • Java
  • SpringBoot
  • 初始化
  • 方式
  • 方法
  • 容器
  • 刷新
  • 監聽
  • 啟動
  • 專案
  • 事件

本文轉載自:六種方式,教你在 SpringBoot 初始化時搞點事情!


前言#

在實際工作中總是需要在專案啟動時做一些初始化的操作,比如初始化執行緒池、提前加載好加密證書.......

那麼經典問題來了,這也是面試官經常會問到的一個問題:有哪些手段在 Spring Boot 專案啟動的時候做一些事情?

方法有很多種,下面介紹幾種常見的方法。

image

1、監聽容器刷新完成擴展點 ApplicationListener<ContextRefreshedEvent>#

ApplicationContext 事件機制是觀察者設計模式實現的,通過 ApplicationEvent 和 ApplicationListener 這兩個介面實現 ApplicationContext 的事件機制。

Spring 中一些內置的事件如下:

  1. ContextRefreshedEvent:ApplicationContext 被初始化或刷新時,該事件被發布。這也可以在 ConfigurableApplicationContext 介面中使用 refresh () 方法來發生。此處的初始化是指:所有的 Bean 被成功裝載,後處理 Bean 被檢測並激活,所有 Singleton Bean 被預實例化,ApplicationContext 容器已就緒可用。
  2. ContextStartedEvent:當使用 ConfigurableApplicationContext (ApplicationContext 子介面)介面中的 start () 方法啟動 ApplicationContext 時,該事件被發布。你可以調查你的資料庫,或者你可以在接受到這個事件後重啟任何停止的應用程式。
  3. ContextStoppedEvent:當使用 ConfigurableApplicationContext 介面中的 stop () 停止 ApplicationContext 時,發布這個事件。你可以在接受到這個事件後做必要的清理的工作。
  4. ContextClosedEvent:當使用 ConfigurableApplicationContext 介面中的 close () 方法關閉 ApplicationContext 時,該事件被發布。一個已關閉的上下文到達生命週期末端;它不能被刷新或重啟。
  5. RequestHandledEvent:這是一個 web-specific 事件,告訴所有 bean HTTP 請求已經被服務。只能應用於使用 DispatcherServlet 的 Web 應用。在使用 Spring 作為前端的 MVC 控制器時,當 Spring 處理使用者請求結束後,系統會自動觸發該事件。

好了,了解上面這些內置事件後,我們可以監聽 ContextRefreshedEvent在 Spring Boot 啟動時完成一些操作,代碼如下:

@Component
public class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent>{
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println(contextRefreshedEvent);
        System.out.println("TestApplicationListener............................");
    }
}

高級玩法#

可以自定事件完成一些特定的需求,比如:郵件發送成功之後,做一些業務處理。

  1. 自定義 EmailEvent,代碼如下:

    public class EmailEvent extends ApplicationEvent{
       private String address;
       private String text;
       public EmailEvent(Object source, String address, String text){
       super(source);
          this.address = address;
          this.text = text;
       }
       public EmailEvent(Object source) {
         super(source);
       }
       //......address和text的setter、getter
    }
    
  2. 自定義監聽器,代碼如下:

    public class EmailNotifier implements ApplicationListener{
       public void onApplicationEvent(ApplicationEvent event) {
         if (event instanceof EmailEvent) {
            EmailEvent emailEvent = (EmailEvent)event;
            System.out.println("郵件地址:" + emailEvent.getAddress());
            System.out.our.println("郵件內容:" + emailEvent.getText());
         } else {
            System.our.println("容器本身事件:" + event);
         }
       }
    }
    
  3. 發送郵件後,觸發事件,代碼如下:

    public class SpringTest {
       public static void main(String args[]){
         ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
         //創建一個ApplicationEvent物件
         EmailEvent event = new EmailEvent("hello","[email protected]","This is a test");
         //主動觸發該事件
         context.publishEvent(event);
       }
    }
    

2、SpringBootCommandLineRunner介面#

當容器初始化完成之後會調用CommandLineRunner中的run()方法,同樣能夠達到容器啟動之後完成一些事情。這種方式和 ApplicationListener 相比更加靈活,如下:

  • 不同的CommandLineRunner實現可以通過@Order()指定執行順序
  • 可以接收從控制台輸入的參數。

下面自定義一個實現類,代碼如下:

@Component
@Slf4j
public class CustomCommandLineRunner implements CommandLineRunner {

    /**
     * @param args 接收控制台傳入的參數
     */
    @Override
    public void run(String... args) throws Exception {
        log.debug("從控制台接收參數>>>>"+ Arrays.asList(args));
    }
}

運行這個 jar,命令如下:

java -jar demo.jar aaa bbb ccc

以上命令中傳入了三個參數,分別是aaabbbccc,這三個參數將會被run()方法接收到。如下圖:

image

源碼分析#

讀過我的文章的鐵粉都應該知道CommandLineRunner是如何執行的,原文:頭禿系列,二十三張圖帶你從源碼分析 Spring Boot 啟動流程~

Spring Boot 加載上下文的入口在org.springframework.context.ConfigurableApplicationContext()這個方法中,如下圖:

image

調用 CommandLineRunner 在callRunners(context, applicationArguments);這個方法中執行,源碼如下圖:

image

3、SpringBootApplicationRunner介面#

ApplicationRunnerCommandLineRunner都是 Spring Boot 提供的,相對於CommandLineRunner來說對於控制台傳入的參數封裝更好一些,可以通過鍵值對來獲取指定的參數,比如--version=2.1.0

此時運行這個 jar 命令如下:

java -jar demo.jar --version=2.1.0 aaa bbb ccc

以上命令傳入了四個參數,一個鍵值對version=2.1.0,另外三個是分別是aaabbbccc

同樣可以通過@Order()指定優先級,如下代碼:

@Component
@Slf4j
public class CustomApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.debug("控制台接收的參數:{},{},{}",args.getOptionNames(),args.getNonOptionArgs(),args.getSourceArgs());
    }
}

通過以上命令運行,結果如下圖:

image

源碼分析#

CommandLineRunner一樣,同樣在callRunners()這個方法中執行,源碼如下圖:

image

4、@PostConstruct註解#

前三種針對的是容器的初始化完成之後做的一些事情,@PostConstruct這個註解是針對Bean的初始化完成之後做一些事情,比如註冊一些監聽器...

@PostConstruct註解一般放在 Bean 的方法上,一旦 Bean 初始化完成之後,將會調用這個方法,代碼如下:

@Component
@Slf4j
public class SimpleExampleBean {

    @PostConstruct
    public void init(){
        log.debug("Bean初始化完成,調用...........");
    }
}

5、@Bean 註解中指定初始化方法#

這種方式和@PostConstruct比較類似,同樣是指定一個方法在 Bean 初始化完成之後調用。

新建一個 Bean,代碼如下:

@Slf4j
public class SimpleExampleBean {

    public void init(){
        log.debug("Bean初始化完成,調用...........");
    }
}

在配置類中通過@Bean實例化這個 Bean,不過@Bean中的initMethod這個屬性需要指定初始化之後需要執行的方法,如下:

@Bean(initMethod = "init")
public SimpleExampleBean simpleExampleBean(){
    return new SimpleExampleBean();
}

6、 InitializingBean介面#

InitializingBean的用法基本上與@PostConstruct一致,只不過相應的Bean需要實現afterPropertiesSet方法,代碼如下:

@Slf4j
@Component
public class SimpleExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet()  {
        log.debug("Bean初始化完成,調用...........");
    }
}

總結#

實現方案有很多,作者只是總結了常用的六種,學會的點個讚。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。