SpringFactoriesLoader类作用详解

 更新时间:2022年10月26日 14:45:22   作者:沫洺  
SpringFactoriesLoader可以加载jar包下META-INF下的spring.factories,把相关接口的实现按照key,value的形式加载到内存,一个接口的多个实现可以按照","进行分割

SpringFactoriesLoader类

介绍

SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用。Spring的自动化配置功能,也与此息息相关。

SpringFactoriesLoader 工厂加载机制是 Spring 内部提供的一个约定俗成的加载方式,只需要在模块的 META-INF/spring.factories 文件中,以 Properties 类型(即 key-value 形式)配置,就可以将相应的实现类注入 Spirng 容器中。

Properties类型格式:

key:value

  • key:是全限定名(抽象类|接口)
  • value:是实现类,多个实现类通过逗号进行分割

spring boot类路径下: META-INFO/spring.factories

方法

返回值方法描述
<T>List<T>loadFactories(Class<T> factoryType,@Nullable ClassLoader classLoader)静态方法 根据接口获取其实现类的实例 该方法返回的是实现类对象列表
List<String>loadFactoryNames(Class<?>) factoryType,@Nullable ClassLoader classLoader)公共静态方法 根据接口获取其实现类的名称 该方法返回的是实现类的类名的列表
public final class SpringFactoriesLoader {
    //文件位置,可以存在多个JAR文件中
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    //用来缓存MultiValueMap对象
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
    private SpringFactoriesLoader() {
    }
    /**
     * 根据给定的类型加载并实例化工厂的实现类
     */
    public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryType, "'factoryType' must not be null");
        //获取类加载器
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        //加载类的全限定名
        List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
        }
        //创建一个存放对象的List
        List<T> result = new ArrayList<>(factoryImplementationNames.size());
        for (String factoryImplementationName : factoryImplementationNames) {
            //实例化Bean,并将Bean放入到List集合中
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
        }
        //对List中的Bean进行排序
        AnnotationAwareOrderComparator.sort(result);
        return result;
    }
    /**
     * 根据给定的类型加载类路径的全限定名
     */
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        //获取名称
        String factoryTypeName = factoryType.getName();
        //加载并获取所有META-INF/spring.factories中的value
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        //根据类加载器从缓存中获取,如果缓存中存在,就直接返回,如果不存在就去加载
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        try {
            //获取所有JAR及classpath路径下的META-INF/spring.factories的路径
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            //遍历所有的META-INF/spring.factories的路径
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                //将META-INF/spring.factories中的key value加载为Prpperties对象
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    //key名称
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        //以factoryTypeName为key,value为值放入map集合中
                        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);
        }
    }
    //实例化Bean对象
    @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);
        }
    }
}

测试

resources下新建META-INF/spring.factories

com.moming.service.IStudentService=\
com.moming.service.impl.StudentServiceImpl1,\
com.moming.service.impl.StudentServiceImpl2

创建业务层接口及实现类

service/IStudentService

service/impl/StudentServiceImpl1

service/impl/StudentServiceImpl2

测试

@SpringBootApplication
public class App{
    public static void main(String[] args) {
        //SpringApplication.run(App.class, args);
        List<String> names = SpringFactoriesLoader.loadFactoryNames(IStudentService.class, ClassUtils.getDefaultClassLoader());
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("===================================");
        List<IStudentService> iStudentServices = SpringFactoriesLoader.loadFactories(IStudentService.class, ClassUtils.getDefaultClassLoader());
        for (IStudentService iStudentService : iStudentServices) {
            System.out.println(iStudentService);
            System.out.println(iStudentService.game());
        }
    }
}

通过以上可以证明,SpringFactoriesLoader会寻找jar包中配置META-INF下的spring.factories配置文件相应Key的value,并根据需要实例化

到此这篇关于SpringFactoriesLoader类作用详解的文章就介绍到这了,更多相关SpringFactoriesLoader内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • Java开发如何把数据库里的未付款订单改成已付款

    Java开发如何把数据库里的未付款订单改成已付款

    这篇文章主要介绍了Java开发如何把数据库里的未付款订单改成已付款,先介绍MD5算法,简单的来说,MD5能把任意大小、长度的数据转换成固定长度的一串字符,实现思路非常简单需要的朋友可以参考下
    2022-11-11
  • Java集合之Map接口与实现类详解

    Java集合之Map接口与实现类详解

    这篇文章主要为大家详细介绍了Java集合中的Map接口与实现类,文中的示例代码讲解详细,对我们学习Java有一定的帮助,感兴趣的可以了解一下
    2022-12-12
  • Java实现MD5加密及解密的代码实例分享

    Java实现MD5加密及解密的代码实例分享

    如果对安全性的需求不是太高,MD5仍是使用非常方便和普及的加密方式,比如Java中自带的MessageDigest类就提供了支持,这里就为大家带来Java实现MD5加密及解密的代码实例分享:
    2016-06-06
  • 使用Idea或Datagrip导入excel数据的方法

    使用Idea或Datagrip导入excel数据的方法

    这篇文章主要介绍了使用Idea或Datagrip导入excel数据的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • SpringBoot实现图片识别文字的四种方式小结

    SpringBoot实现图片识别文字的四种方式小结

    本文主要介绍了SpringBoot实现图片识别文字的四种方式,包括Tess4J,百度智能云,阿里云,腾讯云这四种,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • java文件下载设置中文名称的实例(response.addHeader)

    java文件下载设置中文名称的实例(response.addHeader)

    下面小编就为大家分享一篇java文件下载设置中文名称的实例(response.addHeader),具有很好的参考价值,希望对大家有所帮助
    2017-12-12
  • java实现事件委托模式的实例详解

    java实现事件委托模式的实例详解

    这篇文章主要介绍了java实现事件委托模式的实例详解的相关资料,这里提供实例来说明如何实现改功能,希望能帮助到大家理解这样的模式,需要的朋友可以参考下
    2017-08-08
  • Maven热部署devtools的实现示例

    Maven热部署devtools的实现示例

    本文主要介绍了Maven热部署devtools的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • 如何通过Java监听MySQL数据的变化

    如何通过Java监听MySQL数据的变化

    对于二次开发来说,很大一部分就找找文件和找数据库的变化情况,下面这篇文章主要给大家介绍了关于如何通过Java监听MySQL数据的变化的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-03-03
  • 在Spring中基于Java类进行配置的完整步骤

    在Spring中基于Java类进行配置的完整步骤

    基于Java配置选项,可以编写大多数的Spring不用配置XML,下面这篇文章主要给大家介绍了关于在Spring中基于Java类进行配置的相关资料,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2018-05-05

最新评论