一般用於須要對應用程序上下文進行編程初始化的web應用程序中。例如,根據上下文環境註冊屬性源或激活概要文件。web
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext); }
該接口典型的應用場景是web應用中須要編程方式對應用上下文作初始化。好比,註冊屬性源(property sources)或者針對上下文的環境信息environment激活相應的profile。spring
在一個Springboot應用中,classpath上會包含不少jar包,有些jar包須要在ConfigurableApplicationContext#refresh()調用以前對應用上下文作一些初始化動做,所以它們會提供本身的ApplicationContextInitializer實現類,而後放在本身的META-INF/spring.factories屬性文件中,這樣相應的ApplicationContextInitializer實現類就會被SpringApplication#initialize發現:編程
// SpringApplication#initialize方法,在其構造函數內執行,從而確保在其run方法以前完成 private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));// <=================== setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
而後在應用上下文建立以後,應用上下文刷新(refresh
)以前的準備階段被調用 :springboot
下面列出了一個使用缺省配置的Springboot web應用默認所使用到的ApplicationContextInitializer實現們:app
DelegatingApplicationContextInitializer
使用環境屬性context.initializer.classes指定的初始化器(initializers)進行初始化工做,若是沒有指定則什麼都不作。框架
經過它使得咱們能夠把自定義實現類配置在application.properties裏成爲了可能。ide
ContextIdApplicationContextInitializer
設置Spring應用上下文的ID,會參照環境屬性。至於Id設置爲啥值會參考環境屬性:
spring.application.name
vcap.application.name
spring.config.name
spring.application.index
vcap.application.instance_index函數
若是這些屬性都沒有,ID使用application。spring-boot
ConfigurationWarningsApplicationContextInitializer
對於通常配置錯誤在日誌中做出警告測試
ServerPortInfoApplicationContextInitializer
將內置servlet容器實際使用的監聽端口寫入到Environment環境屬性中。這樣屬性local.server.port就能夠直接經過@Value注入到測試中,或者經過環境屬性Environment獲取。
SharedMetadataReaderFactoryContextInitializer
建立一個SpringBoot和ConfigurationClassPostProcessor共用的CachingMetadataReaderFactory對象。實現類爲:ConcurrentReferenceCachingMetadataReaderFactory
ConditionEvaluationReportLoggingListener
將ConditionEvaluationReport寫入日誌。
以上都是SpringBoot內置的上文啓動器,可見Spring留出的這個鉤子,被SpringBoot發揚光大了。
實際上不只於此,SpringBoot對Spring Framework的事件監聽機制也都有大量的應用~
總結
ApplicationContextInitializer是Spring留出來容許咱們在上下文刷新以前作自定義操做的鉤子,若咱們有需求想要深度整合Spring上下文,藉助它不乏是一個很是好的實現。
隨便瀏覽一下SpringBoot的源碼可知,它對Spring特徵特性的使用,均是很是的流暢且深度整合的。因此說SpringBoot易學難精的最大攔路虎:實際上是對Spring Framework系統性的把握~
Tips:spring-test包裏有個註解org.springframework.test.context.ContextConfiguration它有個屬性能夠指定ApplicationContextInitializer輔助集成測試時候的自定義對上下文進行預處理~
先定義ApplicationContextInitializer,以下:
package com.transsnet.palmpay.controller; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.annotation.Order; //@Order的value值越小->越早執行。注:在類上標註,不是方法上 @Order(111) public class ApplicationContextInitializer1 implements ApplicationContextInitializer { @Override public void initialize(ConfigurableApplicationContext applicationContext) { // 打印容器裏面有多少個bean System.out.println("bean count=====" + applicationContext.getBeanDefinitionCount()); // 打印人全部 beanName System.out.println(applicationContext.getBeanDefinitionCount() + "個Bean的名字以下:"); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanName : beanDefinitionNames) { System.out.println(beanName); } } }
啓動類裏手動增長initializer
@SpringBootApplication @EnableConfigServer @EnableDiscoveryClient public class ConfigServer { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(ConfigServer.class); // 方法一:添加自定義的 ApplicationContextInitializer 實現類的實例(註冊ApplicationContextInitializer) springApplication.addInitializers(new ApplicationContextInitializer1()); ConfigurableApplicationContext context = springApplication.run(args); context.close(); } }
結果:
對於這種方式是經過DelegatingApplicationContextInitializer這個初始化類中的initialize方法獲取到application.properties中context.initializer.classes對應的類並執行對應的initialize方法。只須要將實現了ApplicationContextInitializer的類添加到application.properties便可。以下:
一、先定義先定義ApplicationContextInitializer,同1.1
二、在application.properties中定義:
一、先定義先定義ApplicationContextInitializer,同1.1
二、而後在項目下的resources下新建META-INF文件夾,文件夾下新建spring.factories文件
org.springframework.context.ApplicationContextInitializer=com.dxz.ApplicationContextInitializer1
這個加載過程是在SpringApplication中的getSpringFactoriesInstances()方法中直接加載並實例後執行對應的initialize方法。代碼以下:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
springboot中自帶的DelegatingApplicationContextInitializer類的排序值爲0,是springboot自帶的ApplicationContextInitializer中排序最小,最早執行的類。(若是ApplicationContextInitializer沒有實現Orderd接口,那麼其排序值默認是最大,最後執行)
因此能夠獲得其執行順序以下
1.若是咱們經過DelegatingApplicationContextInitializer委託來執行咱們自定義的ApplicationContextInitializer,那麼咱們自定義的ApplicationContextInitializer的順序必定是在系統自帶的其餘ApplicationContextInitializer以前執行。
2.若是咱們經過SpringApplication實例對象調用addInitializers方法加入自定義的ApplicationContextInitializer,那麼spring-boot自帶的ApplicationContextInitializer會先按順序執行,再執行咱們手動添加的自定義ApplicationContextInitializer(按照添加順序執行),最後執行spring-boot自帶的其餘ApplicationContextInializer
3.若是咱們建立本身的spring.factories文件,添加配置加入咱們自定義的ApplicationContextInitializer,那麼咱們自定義的ApplicationContextInitializer會和spring-boot自帶的ApplicationContextInitializer放在一塊兒進行排序執行。