ApplicationContextInitializer主要用於初始化 spring上下文 ConfigurableApplicationContext.
所有的初始化器將在spring創建完ConfigurableApplicationContext之后緊接着執行.並且在ConfigurableApplicationContext#refresh()方法之前.
通常用於配置環境 ConfigurableApplicationContext#getEnvironment(), 或者注冊各種前置處理器context.addBeanFactoryPostProcessor()等.
/** * Callback interface for initializing a Spring {@link ConfigurableApplicationContext} * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}. * * <p>Typically used within web applications that require some programmatic initialization * of the application context. For example, registering property sources or activating * profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment() * context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support * for declaring a "contextInitializerClasses" context-param and init-param, respectively. * * <p>{@code ApplicationContextInitializer} processors are encouraged to detect * whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been * implemented or if the @{@link org.springframework.core.annotation.Order Order} * annotation is present and to sort instances accordingly if so prior to invocation. * * @author Chris Beams * @since 3.1 * @param <C> the application context type * @see org.springframework.web.context.ContextLoader#customizeContext * @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM * @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses * @see org.springframework.web.servlet.FrameworkServlet#applyInitializers */ public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext); }
下面是注冊初始化器的三種方式:
1.通過在項目中建立 META-INF/spring.factories 文件 並在文件中添加類信息:
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
ps: springboot在啟動的時候會自動全項目(包括引入的所有jar包)掃描 META-INF目錄下的spring.factories文件, 然后一次性全部讀取到 緩存中 ,根據ClassLoader,進行區分不同緩存. 然后會執行無參構造函數進行實例化, 排序 存儲在org.springframework.boot.SpringApplication#initializers變量中.
* The location to look for factories. * <p>Can be present in multiple JAR files. */ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// 緩存 private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>(); private SpringFactoriesLoader() { }
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader); // 不同classLoader對應不同的緩存
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();//多值Map key對應要加載的classType,value為List列表,存儲spring.factories中classType對應的具體class全限定名
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
2.在執行SpringApplication run方法之前進行自定義添加到SpringApplication#initializers中
public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(SampleActiveMQApplication.class); springApplication.addInitializers(new MyApplicationContextInitializer); //手動添加
springApplication.run(args);
}
3.在配置文件中配置
context.initializer.classes = MyApplicationContextInitializer1,MyApplicationContextInitializer2,MyApplicationContextInitializer3 //注意替換為全限定名 並用逗號隔開
ps:
在springboot自帶的包中,有一個自帶的 委托初始化器DelegatingApplicationContextInitializer,因為他的本身的Order是最靠前的,所以在執行所有的初始化器的時候會首先執行這個.
由上圖可知 在加載並實例化完所有的配置的類后,首先進行了排序,並接着就遍歷執行了initialize(context)方法,也就是說在配置文件中配置的話 會優先於其他的配置方式執行.