springboot自动配置之SpringFactoriesLoader分析

直接上代码

package org.springframework.core.io.support;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.*;

import java.io.IOException;
import java.net.URL;
import java.util.*;

/**
 * 1. spring内部使用的工厂类
 * 2. 用来加载并缓存每个jar包下的META-INF/spring.factories中配置的所有的类
 * 3. 这里用到了工厂模式
 * >>>> 通过传入的Class实例,来通过反射创建实例对象
 * 4. 工厂内部结构
 * >>>> 主要是一个cache来缓存当前工厂可以创建哪些实例的对象
 *
 * @see SpringFactoriesLoader#cache
 */
public final class SpringFactoriesLoader {

    /**
     * spring.factories位置
     */
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    /**
     * 打印相关日志
     */
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

    /**
     * 此cache中缓存有当前项目中所有jar包下META-INF/spring.factories中配置的所有类,即当前工厂可以创建的实例
     * 1. key 为类加载器 一般情况下使用的系统类加载器
     * 2. value 为MultiValueMap
     * >>>> 此map的特点是 key为String,value为一个List
     * >>>> 此处key保存的是: 抽象描述类的Class实例,与传入的Class实例相对应,通过此key来找当前工厂要加载的实例
     * >>>> 此处value保存的是: 抽象描述类的具体实现类,也就是当前工厂可以加载的实例
     * >>>> 例如当key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
     * 那么value就是在org.springframework.boot.autoconfigure.EnableAutoConfiguration下配置的一个一个具体的自动配置类的集合
     * >>>> 不论是key还是value保存的都是全类名
     */
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

    private SpringFactoriesLoader() {
    }


    /**
     * 根据传入的Class实例,从{@link #cache}通过反射 加载对应的实例对象
     *
     * @param factoryType 与当前要加载的实例相对应的Class对象
     * @param classLoader 类加载器
     */
    public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
        // 1. 校验factoryType
        Assert.notNull(factoryType, "'factoryType' must not be null");
        // 2. 获取类加载器
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        // 3. 从缓存cache中获取所有可以加载的实例的全类名
        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
        // 4. 打印相关日志
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
        }
        // 5. 通过反射创建实例对象 并保存到集合中
        List<T> result = new ArrayList<>(factoryImplementationNames.size());
        for (String factoryImplementationName : factoryImplementationNames) {
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
        }
        // 6. 对实例化后的对象进行排序
        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

    /**
     * 根据传入的Class实例 从{@link #cache}中获取所有的对应实例的全类名
     *
     * @param factoryType 与当前要加载的实例相对应的Class对象
     * @param classLoader 类加载器
     * @return 所有与传入的Class对象相对应的实例的全类名
     */
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        // 1. 获取传入的Class对象的全类名
        String factoryTypeName = factoryType.getName();
        // 2. 获取所有的对应实例的全类名 如果不存在则返回空
        // loadSpringFactories() 对工厂进行初始化(会初始化cache)
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    /**
     * 两个功能
     * 1. 初始化工厂 即通过传入的类加载器{@code classLoader}来将当前项目中所有jar包下
     * META-INF/spring.factories中的所有要加载的类缓存到{@link #cache}中
     * 2. 从cache中获取所传入的{@code classLoader}类加载器所对应的可加载的实例
     *
     * @param classLoader 类加载
     */
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 1. 先缓存中获取
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        // 2. 如果缓存中没有 就对缓存进行初始化
        try {
            // 2.1 所有jar包下的spring.factories的绝对路径 并保存为urls
            Enumeration<URL> urls = (classLoader != null ?
                    // 如果传入类加载器不为null  那么就通过传入类加载器来获取 META-INF/spring.factories下的资源
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    // 否则通过默认的系统类加载器来获取
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                // 2.2 获取jar包下的spring.factories绝对路径
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                // 2.3 加载spring.factories的资源 并转换为Properties
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                // 将properties的k,v放到result中
                // properties中保存k,v的方式是:key为抽象描述类的Class实例 value为多个具体实现类并以逗号分割
                // 例如
                // key: org.springframework.boot.autoconfigure.EnableAutoConfiguration
                // value: com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
                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());
                    }
                }
            }
            // 2.4 将加载的资源放到缓存中
            cache.put(classLoader, result);
            // 2.5 返回当前类加载器所对应的资源
            return result;
        } catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

    /**
     * 当前工厂通过反射 真实的去创建对应的实例对象
     *
     * @param factoryImplementationName 要创建的实例的全类名(根据这个来通过反射进行创建)
     * @param factoryType               要创建的实例所对应的Class实例
     * @param classLoader               类加载器
     * @param <T>                       要创建的实例的类型
     * @return 创建好的实例
     */
    @SuppressWarnings("unchecked")
    private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
        try {
            Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
            if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
                throw new IllegalArgumentException(
                        "Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
            }
            return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
        } catch (Throwable ex) {
            throw new IllegalArgumentException(
                    "Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
                    ex);
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值