zoukankan      html  css  js  c++  java
  • 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,
    ....
  • 相关阅读:
    4.2 [单选]2011年12月30日,国务院办公厅公布的三网融合第二阶段试点城市有()个 - 关于三网融合(主讲:凌捷)笔记
    4.1 [单选]两化融合中的两化是指 - 关于两化融合(主讲:凌捷)笔记
    [单选]物联网产业链的主要产品不包括下列哪一项 - 关于物联网(主讲:柳毅)笔记
    关于云计算(主讲:柳毅)笔记
    [转]iis7.5+win2008 出现 HTTP Error 503. The service is unavailable.
    delphi中Bitmap位图与base64字符串相互转换
    HTTP 错误 404.2
    这3周以来的面试总结(C#/.net 智能硬件/物联网)
    2017.4找工作面试记录-第三周(3)
    2017.4找工作面试记录-第三周(2)--金蝶
  • 原文地址:https://www.cnblogs.com/chenxingyang/p/15439536.html
Copyright © 2011-2022 走看看