Spring的SPI机制【spring.factories】

从自动配置开始看一下

组合注解@SpringBootApplication中的注解@EnableAutoConfiguration

 

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

 

类AutoConfigurationImportSelector,这里的解析需要知道@Import注解的解析及相关接口,这里略,下面是简略流程

AutoConfigurationImportSelector#selectImports
    --> #getAutoConfigurationEntry
        --> # getCandidateConfigurations 
            --> SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class)

 

 


 

接着就是从META-INF/spring.factories文件按规则加载类的流程了

    // 加载关心的类型factoryType配置的对应的类, 并实例化这些类
    public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
        // 加载器处理...

        // 根据关心的类型得到所有的name, 一般是全类名列表
        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
        List<T> result = new ArrayList<>(factoryImplementationNames.size());
        for (String factoryImplementationName : factoryImplementationNames) {
            // 无惨构造函数初始化
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
        }
        // 排序
        AnnotationAwareOrderComparator.sort(result);
        // 得到实例列表
        return result;
    }
    
    
    // 加载关心的类型factoryType配置的对应的类, 得到全类名列表
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        // 类加载器
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        // 获取全类名
        String factoryTypeName = factoryType.getName();
        // 具体加载逻辑, 返回的是Map<String, List>, 前者即为loadFactoryNames, 后者为相关类
        return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
    // 一次性加载加载器能加载的所有 spring.factories 文件并解析
    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        // 缓存, 类加载器作为键, 每个类加载器只加载一次
        Map<String, List<String>> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        result = new HashMap<>();
        try {
            // "META-INF/spring.factories", 类路径、jar包等内的
            Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            while (urls.hasMoreElements()) {
                // 找到一个文件
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                // 加载成为 Properties 实例, =隔开的键值对
                // 而 spring.factories 的格式为 k=val1,val2,...
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                // 键, 一般为关心的类型全类名
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    // 全类名
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    // ,隔开的值列表解析
                    String[] factoryImplementationNames =
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                    for (String factoryImplementationName : factoryImplementationNames) {
                        // computeIfAbsent 值为 List, k对应没有则new List, 有则返回并add
                        result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                .add(factoryImplementationName.trim());
                    }
                }
            }

            // k:factoryType, v:implementations, 即List去重, 那为什么不使用Set存储呢
            result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                    // 不可变对象
                    .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
            // 缓存
            cache.put(classLoader, result);
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
        return result;
    }
  1. 一次性加载、解析并缓存指定加载器能加载的META-INF/factories文件
  2. 提供获取所关心的类型的配置名称列表、实例列表的API

META-INF/factories文件示例,为spring-boot-autoconfigure这个jar包下的

键值均是类权限定名称,当根据某Class类型取时,得到的是配置列表(类权限定名称列表或实例列表)

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
....

 

posted @ 2021-10-22 16:20  YangDanMua  阅读(224)  评论(0编辑  收藏  举报