前言
当我们创建一个SpringBoot应用,然后在main方法中打开SpringBoot的源码时,如下所示:
SpringApplication.run(Application.class, args); // Application是自定义的类名
进入SpringBoot中的run方法,可以看到SpringBoot的启动过程其实是包含两个过程,即SpringApplication类的初始化,以及run方法执行,如下所示:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
先来看SpringApplication类的构造方法,代码如下:
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
在上述构造函数中,有两个重要的操作,一个是设定初始化器;一个是设定监听器。接下来我们就这是进入ApplicationContextInitializer接口的介绍。
1、ApplicationContextInitializer接口的实现类
先来看一下ApplicationContextInitializer接口中的具体内容:
/*
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
* Sprng上下文的回调接口,在refresh()之前执行
*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
在接口中只有一个方法initialize(),主要的作用就是对Spring的上下文进行初始化。
由前言中可以看出,ApplicationContextInitializer接口的实现类主要是在SpringApplication类的构造函数中进行加载和初始化,这里我们先要看一个方法getSpringFactoriesInstances,具体实现如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 根据type来加载spring.factories中配置的类路径,具体实现如下;【这里的type=ApplicationContextInitializer.class】
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据类路径来实例化各类,具体实现如下
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 从META-INF/spring.factories中加载类的路径
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
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);
}
}
看一下与ApplicationContextInitializer接口相关类路径在Spring.factories文件中的存储方式:
org.springframework.context.ApplicationContextInitializer=[org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer, org.springframework.boot.context.ContextIdApplicationContextInitializer, org.springframework.boot.context.config.DelegatingApplicationContextInitializer, org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer, org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer, org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer, org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener],
是一个数组,当执行完上述的代码后,这7个子类都会被加载并实例化,另外如果有其他自定义的一些初始化器也会加载并实例化。
在ApplicationContextInitializer接口的子类中重要的方法就是重写接口中的initialize,在SpringBoot启动的时候真正执行是在run()方法内的prepareContext(…)方法内执行的。
2、自定义ApplicationContextInitializer接口子类
自定义一个ApplicationContextInitializer接口的实现类,如下所示:
public class SelfDefinitionInitializer implements ApplicationContextInitializer{
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("容器中Bean的数量为:"+applicationContext.getBeanDefinitionCount());
}
}
如果要将SelfDefinitionInitializer添加到系统中可以有三种方式:
1、修改系统的启动类,进行如下的修改:
@SpringBootApplication
public class LearnSpringbootBean1Application {
public static void main(String[] args) {
SpringApplication springApplication=new SpringApplication(LearnSpringbootBean1Application.class);
springApplication.addInitializers(new SelfDefinitionInitializer());
springApplication.run(args);
// SpringApplication.run(LearnSpringbootBean1Application.class, args);
}
}
2、系统配置文件(application.properties)中进行如下配置:
context.initializer.classes=com.learn.initializer.SelfDefinitionInitializer
# 如果有多个的话,使用,号隔开
3、创建一个META-INF/spring.factories文件,然后添加如下配置:
org.springframework.context.ApplicationContextInitializer=\
com.learn.initializer.SelfDefinitionInitializer
然后启动项目,都可以得到如下执行结果: