springboot加载spring.factories文件
从springboot的main方法开始逐行分析
开始分析
public static void main(String[] args)
{
SpringApplication.run(DemoApplication.class, args);
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
一路跟进,到如下的方法中,继续进入到 new SpringApplication(primarySources) 方法中
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
紧接着进入到如下的方法中,看到 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 这一行,注意getSpringFactoriesInstances(ApplicationContextInitializer.class)方法,这就是接下来的加载spring.factories的主要方法
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();
}
在跟进到getSpringFactoriesInstances(Class type)方法中,该方法首先获取到类加载器,然后在第二行,注意SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,根据名称我们大概可以猜到,该方法就是加载spring.factories文件的主要方法,我们进去继续分析
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
{
ClassLoader classLoader = getClassLoader();
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
第一行代码不用关注,这是在加载时根据类型,获取到对应的需要被创建的实际类;我们主要看loadSpringFactories(classLoader)方法,该方法会为我们加载spring.factories文件
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
{
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
接下来这就是springboot加载spring.factories文件的最主要的方法;此处定义的 FACTORIES_RESOURCE_LOCATION 就是 META-INF/spring.factories ,首先从缓存cache中获取,如果没有则通过当前类加载器去加载对应的spring.factories文件;紧接着,创建了一个MultiValueMap对象,该对象一个key可以对应多个value,当一个key对应的value为多个时,value就是一个List的结构;紧接着遍历对应的urls,将对应的url封装成UrlResource对象,然后调用PropertiesLoaderUtils 的 loadProperties 方法加载对应的spring.factories文件;然后遍历properties对象,将逗号分隔的字符串分割成字符数组,然后再遍历,将对应的字符串添加到MultiValueMap对象中,当遍历完成后将result直接添加到cache中,此后每一次进入到该方法中都会直接从cache缓存中获取,而不会在重新读取我们的spring.factories文件,到此springboot加载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 {
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);
}
}