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)方法,也就是说在配置文件中配置的话 会优先于其他的配置方式执行.