title: 【轉載】六種方式,教你在 SpringBoot 初始化時搞點事情!
date: 2021-08-24 15:19:00
comment: false
toc: true
category:
- Java
tags: - 轉載
- Java
- SpringBoot
- 初始化
- 方式
- 方法
- 容器
- 刷新
- 監聽
- 啟動
- 專案
- 事件
本文轉載自:六種方式,教你在 SpringBoot 初始化時搞點事情!
前言#
在實際工作中總是需要在專案啟動時做一些初始化的操作,比如初始化執行緒池、提前加載好加密證書.......
那麼經典問題來了,這也是面試官經常會問到的一個問題:有哪些手段在 Spring Boot 專案啟動的時候做一些事情?
方法有很多種,下面介紹幾種常見的方法。
1、監聽容器刷新完成擴展點 ApplicationListener<ContextRefreshedEvent>
#
ApplicationContext 事件機制是觀察者設計模式實現的,通過 ApplicationEvent 和 ApplicationListener 這兩個介面實現 ApplicationContext 的事件機制。
Spring 中一些內置的事件如下:
ContextRefreshedEvent
:ApplicationContext 被初始化或刷新時,該事件被發布。這也可以在 ConfigurableApplicationContext 介面中使用 refresh () 方法來發生。此處的初始化是指:所有的 Bean 被成功裝載,後處理 Bean 被檢測並激活,所有 Singleton Bean 被預實例化,ApplicationContext 容器已就緒可用。ContextStartedEvent
:當使用 ConfigurableApplicationContext (ApplicationContext 子介面)介面中的 start () 方法啟動 ApplicationContext 時,該事件被發布。你可以調查你的資料庫,或者你可以在接受到這個事件後重啟任何停止的應用程式。ContextStoppedEvent
:當使用 ConfigurableApplicationContext 介面中的 stop () 停止 ApplicationContext 時,發布這個事件。你可以在接受到這個事件後做必要的清理的工作。ContextClosedEvent
:當使用 ConfigurableApplicationContext 介面中的 close () 方法關閉 ApplicationContext 時,該事件被發布。一個已關閉的上下文到達生命週期末端;它不能被刷新或重啟。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............................");
}
}
高級玩法#
可以自定事件完成一些特定的需求,比如:郵件發送成功之後,做一些業務處理。
-
自定義 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 }
-
自定義監聽器,代碼如下:
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); } } }
-
發送郵件後,觸發事件,代碼如下:
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、SpringBoot
的CommandLineRunner
介面#
當容器初始化完成之後會調用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
以上命令中傳入了三個參數,分別是aaa
、bbb
、ccc
,這三個參數將會被run()
方法接收到。如下圖:
源碼分析#
讀過我的文章的鐵粉都應該知道CommandLineRunner
是如何執行的,原文:頭禿系列,二十三張圖帶你從源碼分析 Spring Boot 啟動流程~
Spring Boot 加載上下文的入口在org.springframework.context.ConfigurableApplicationContext()
這個方法中,如下圖:
調用 CommandLineRunner 在callRunners(context, applicationArguments);
這個方法中執行,源碼如下圖:
3、SpringBoot
的ApplicationRunner
介面#
ApplicationRunner
和CommandLineRunner
都是 Spring Boot 提供的,相對於CommandLineRunner
來說對於控制台傳入的參數封裝更好一些,可以通過鍵值對來獲取指定的參數,比如--version=2.1.0
。
此時運行這個 jar 命令如下:
java -jar demo.jar --version=2.1.0 aaa bbb ccc
以上命令傳入了四個參數,一個鍵值對version=2.1.0
,另外三個是分別是aaa
、bbb
、ccc
。
同樣可以通過@Order()
指定優先級,如下代碼:
@Component
@Slf4j
public class CustomApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.debug("控制台接收的參數:{},{},{}",args.getOptionNames(),args.getNonOptionArgs(),args.getSourceArgs());
}
}
通過以上命令運行,結果如下圖:
源碼分析#
和CommandLineRunner
一樣,同樣在callRunners()
這個方法中執行,源碼如下圖:
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初始化完成,調用...........");
}
}
總結#
實現方案有很多,作者只是總結了常用的六種,學會的點個讚。