作用
ApplicationContextInitializer是Spring的一個擴展接口,在spring框架刷新上下文(refresh方法)之前調用ApplicationContextInitializer的實現類完成一些ApplicationContext子類的初始化工作,例如加入一個數據源或者激活profile的環境,ApplicationContextInitializer實現類通常會實現一個Ordered接口或者使用@Order接口進行排序。
配置方式
spring.factories
通過在META-INF目錄下的spring.factories配置org.springframework.context.ApplicationContextInitializer的實現類,將ApplicationContextInitializer的實現類加載到內存中,如下圖所示:
springApplication
通過調用springApplication的addInitializers方法將自定義的實現類加載到內存,如下圖所示
application.properties
通過在application.properties文件中配置context.initializer.classes屬性加載
自定義實現類
自定義一個ApplicationContextInitializer實現類,加入一個數據源,通過META-INF/spring.factories加載到內存,代碼如下所示:
@Order(1)
public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
Map<String, Object> map = new HashMap<>();
map.put("feidao", "zjl");
MapPropertySource source = new MapPropertySource("firstInitializer", map);
environment.getPropertySources().addLast(source);
System.out.println("run firstInitializer");
}
}
org.springframework.context.ApplicationContextInitializer=com.zjl.spring.initializer.FirstInitializer
實現一個SpringService,注入applicationContext,通過getProperties方法獲取feidao屬性。
@Component
public class SpringService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public String getProperties() {
return applicationContext.getEnvironment().getProperty("feidao");
}
}
在Application類實現ApplicationContextAware,通過applicationContext獲取SpringService類,實現如下:
package com.zjl.spring;
import com.zjl.spring.service.SpringService;
import org.springframework.beans.BeansException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@SpringBootApplication
public class Application implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.run(args);
SpringService springService = getBean(SpringService.class);
System.out.println(springService.getProperties());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> targetClz) {
return applicationContext.getBean(targetClz);
}
}
啓動spring,結果:打印出了run firstInitializer和feidao屬性的值zjl。
源碼
注入
spring.factories
在Spring源碼之SpringFactoriesLoader(一)中我們已經知道配置在META-INF/spring.factories中的屬性會通過SpringFactoriesLoader加載到內存,在這裏我們就不詳細說明了。
springApplication
通過調用springApplication的addInitializers方法將實現類加入initializer的lis中,源碼如下:
application.properties
通過在application.properties文件中配置context.initializer.classes屬性加載,我們可以在工程中搜索該配置,看看哪裏用到了,如下圖
可以看到DelegatingApplicationContextInitializer類中用到了這個配置,而且這個類也實現了ApplicationContextInitializer接口,我們可以一起看看該類的initialize方法。
首先通過getInitializerClasses方法加載到所有的context.initializer.classes配置的類,
通過applyInitializerClasses實例化加載進來的類,並且循環調用
所以通過context.initializer.classes配置的ApplicationContextInitializer實現類會通過DelegatingApplicationContextInitializer加載並且循環調用。
調用
通過在自定義實現的FirstInitializer的initialize方法中打上斷點,查看調用鏈,如下圖所示。
根據調用鏈進行分析,入口在springApplication.run方法中的prepareContext方法進入。
在prepareContext方法中調用了applyInitializers方法
進入applyInitializers中,可以看到拿到了所有的initializer,進行循環調用initialize方法。
應用
ApplicationContextInitializer一般是給web應用做初始化使用,還有就是加載一些數據源,爲應用拉取外部的一些配置,例如Apollo(動態配置中心)的配置如果也想通過@Value註解加載,就可以實現一個ApplicationContextInitializer,在應用啓動時把配置從apollo拉取下來加載到spring中,如下是ApolloApplicationContextInitializer的實現
總結
通過分析ApplicationContextInitializer,我們可以知道通過ApplicationContextInitializer我們可以給容器做一些初始化工作或者加載一些外部的配置屬性。