在實際工作開發中,我們經常會遇到在項目啟動的時候對應用進行一些初始化的操作,例如對于線程池的初始化、對于證書的初始化等等操作。那麼在面試的時候面試官也會問到在SpringBoot中的動态加載配置相關的内容,那麼下面我們就來看看在Spring Boot中對于配置動态重新整理常用的幾種方式。
第一、通過監聽容器重新整理完擴充點
我們知道在Spring ApplicationContext中提供了基于觀察者模式的事件處理機制,通過ApplicationEvent 和ApplicationListener兩個接口來實作ApplicationContext的事件機制,當然在Spring中也提供了一些内置的事件機制。
ContextRefreshedEvent: ApplicationContext被初始化或者發生重新整理操作的時候,這個事件就會被觸發,當然也可以在ConfigurableApplicationContext接口中使用refresh()方法來觸發。當然這裡需要注意的一點是這裡所指的初始化是當所有的Bean對象都被成功裝載,後處理Bean被檢測并且激活,所有的Singleton Bean都被預執行個體化,這個時候ApplicationContext容器就可以就緒使用了。
ContextStartedEvent:當使用ConfigurationApplicationContext 接口中的start方法啟動ApplicationContext的時候,該事件被觸發。
ContextStoppedEvent:當使用ConfigurationApplicationContext 接口中的stop方法停止ApplicationContext的時候,該事件被觸發。
ContextCloseEvent:當使用ConfigurationApplicationContext 接口的close方法關閉ApplicationContext的時候,該事件被觸發。
RequesthandledEvent:這是一個web-specific事件,用來告知Bean Http請求已經被服務。隻能應用于使用DispatcherServlet的Web應用。在使用Spring 作為前端的MVC控制器的時候,當Spring處理使用者請求結束之後,系統自動觸發該事件。
可以做個簡單的小測試。啟動Spring Boot之後會看到事件内容被列印了。
@Component
public class TestApplicationListener implements
ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("觸發了ContextRefreshedEvent事件");
}
}
第二、使用Spring Boot的CommandLineRunner接口
在Spring容器被初始化之後會調用CommandLineRunner接口中的run方法,同樣通過這種方式也可以實作動态參數傳遞,而且這種方式要比通過ApplicationListener的方式相對靈活。如下
@Component
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println(Arrays.asList(args));
}
}
可以在項目啟動的時候通過指令行傳入參數
第三、通過Spring Boot的ApplicationRunner接口
ApplicationRunner和CommandLineRunner都是Spring Boot提供的接口,相對于CommandLineRunner來說對控制台傳入的參數封裝的相對友好一些,可以通過鍵值對的方式來進行參數的傳遞。
@Component
public class TestApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(args.getOptionNames());
System.out.println(args.getNonOptionArgs());
System.out.println(args.getSourceArgs());
}
}
可以采用同樣的方式進行參數的傳入測試。
第四、使用@PostConstruct 注解
前面三種方式都是通過容器初始化完成之後做的事情,而@PostConstruct 注解則是針對Bean初始化完成之後來做一些事情,例如對于一些監聽器的注冊、對于一些初始化對象調用。
@PostConstruct 注解一般都是放在Bean的方法上,一旦Bean對象被初始化了,就會調用這個方法對方法中的的内容進行調用。
@Component
public class TestBean {
@PostConstruct
public void init(){
System.out.println("Bean對象被初始化了");
}
}
從代碼執行的結果來看,Bean對象初始化的調用要比容器初始化的調用要早,這是由于容器要等待所有的Bean都被加載才能算是初始化完成,這個在前面的内容中我們介紹過。
第五、使用@Bean注解的指定的初始化方法
在使用XML配置檔案的時候我們用到過一個init-method的屬性,這個屬性就是用來指定加載Bean完成之後調用那個方法這裡,我們也是通過注解版的這個屬性來實作這個操作,整個的操作的過程與@PostConstruct注解類似。
首先需要定義一個測試Bean對象
public class TestInitBean {
public void init(){
System.out.println("使用Bean注解的InitMethod屬性");
}
}
定義配置檔案來注入Bean對象,并且指定初始化方法。
@Configuration
public class TestConfig {
@Bean(initMethod = "init")
public TestInitBean testInitBean(){
return new TestInitBean();
}
}
第六、使用InitializingBean接口
InitializingBean的基本用法與@PostConstruct 注解的用法類似,不過就是需要實作afterPropertiesSet()方法,具有一定的代碼侵入性。
@Component
public class InitImplTestBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Bean初始化完成");
}
}
總結
通過上面的幾種方式,我們可以實作動态配置的加載,當然可以實作動态配置加載的方式還有很多不僅局限于這種方式,希望有興趣的讀者多多留意。