SpringFactoriesLoader 是Spring提供的通用的加载 classPath目录下的各个JAR包中的META-INF目录下的 spring.factories 文件的工具,该文件的格式应该符合properties的文件的格式,如果多个值可以通过逗号分割 比如 example.MyService=example.MyServiceImpl1,example.MyServiceImpl2, 全类名为 org.springframework.core.io.support.SpringFactoriesLoader

    总结来说,如下:

    • 框架内部使用的通用工厂加载机制
    • 从ClassPath目录下读取多个JAR报的指定文件
    • 文件内容必须是KV,即properties格式
    • key是全限定名(抽象类或者接口),value是实现方式, 多个实现,使用逗号分割

    其主要流程为:

    SpringFactoriesLoader - 图1

    1. public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    2. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    3. String factoryTypeName = factoryType.getName();
    4. // 根据factorynName文件名过滤出Value信息
    5. return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    6. }
    7. // 查找spring.factories 文件对象信息
    8. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    9. MultiValueMap<String, String> result = cache.get(classLoader);
    10. if (result != null) {
    11. return result;
    12. }
    13. try {
    14. // 查询目录下的spring.factories 文件路径信息
    15. Enumeration<URL> urls = (classLoader != null ?
    16. classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    17. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    18. result = new LinkedMultiValueMap<>();
    19. while (urls.hasMoreElements()) {
    20. URL url = urls.nextElement();
    21. UrlResource resource = new UrlResource(url);
    22. // 构造Properties
    23. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    24. for (Map.Entry<?, ?> entry : properties.entrySet()) {
    25. String factoryTypeName = ((String) entry.getKey()).trim();
    26. for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    27. result.add(factoryTypeName, factoryImplementationName.trim());
    28. }
    29. }
    30. }
    31. cache.put(classLoader, result);
    32. return result;
    33. }
    34. catch (IOException ex) {
    35. throw new IllegalArgumentException("Unable to load factories from location [" +
    36. FACTORIES_RESOURCE_LOCATION + "]", ex);
    37. }
    38. }

    比如SpringBoot应用获取ApplicationContextInitializer的实例就使用如下的代码:

    1. getSpringFactoriesInstances(ApplicationContextInitializer.class);
    2. private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    3. ClassLoader classLoader = getClassLoader();
    4. // 获取实现的类限定名
    5. Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    6. // 实例化对象
    7. List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    8. AnnotationAwareOrderComparator.sort(instances);
    9. return instances;
    10. }