- 最开始的入口,我们最熟悉的SpringApplication.run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
//重载
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
//重载x2
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//这里创建了一个SpringApplication的实例对象,然后又调用实例的run方法
return new SpringApplication(primarySources).run(args);
}
- 在spring boot加载的时候经常会访问spring.factories,第一次访问在创建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();
//getSpringFactoriesInstances便是spring封装的获取spring.factories中某一项的方法
//这里获取其的是键为 org.springframework.context.ApplicationContextInitializer 的值列表
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
3.开始进入正题
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
// 一个重载方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates 使用set防止值重复
// SpringFactoriesLoader.loadFactoryNames 方法获取 type 对应的值列表
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//这个方法获取所有值的实例对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//根据 @Ordered 和 @Priority 注解排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
- 进入 SpringFactoriesLoader.loadFactoryNames 方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
//loadSpringFactories 方法获取所有 spring.factories 中所有的信息
//loadSpringFactories 方法返回的是一个 键为string,值为list的map,map中存储的就是所有spring.factories聚合后的内容
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
- 进入 loadSpringFactories 方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//cache是个concurrentHashMap,这种经常需要的数据当然是要上缓存的(我暂时不知道用map做缓存干嘛,可能会有多个类加载器?)
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
//没有缓存的话就获取一遍
try {
//spring boot能获取classpath下每一项的同一个路径的文件的核心就在这里,调用类加载器的 getResources 方法
//FACTORIES_RESOURCE_LOCATION 是一个值为 META-INF/spring.factories 的常量,定义spring.factories的位置
//不同的类加载器对于 getResources 方法都有不同的实现(没细看,其实)
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
//遍历每一个 spring.factories
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//生成 Properties 对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//保存所有键值对
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
//StringUtils.commaDelimitedListToStringArray 按逗号分割
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);
}
}
总结:spring boot 加载spring.factories其实就是利用了类加载器的 getResources 方法,获取classpath下每一项的spring.factories文件,然后将所有factores文件的所有键值对保存在一个map中,要用是按key获取便是