我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SpringBoot借助spring.factories文件跨模块实例化Bean

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SpringBoot借助spring.factories文件跨模块实例化Bean

1. 前言

SpringBoot在包扫描时,并不会扫描子模块下的内容,这样就使得我们的子模块中的Bean无法注入到Spring容器中。SpringBoot就为我们提供了spring.factories这个文件,让我们可以轻松的将子模块的Bean注入到我们的Spring容器中,本篇文章我们就一起探究一下spring.factories 跨模块实例化Bean的原理。

我们在SpringBoot项目为何引入大量的starter?如何自定义starter?文章中也讲到构建自己构建starter,其中spring.factories就起到重要的作用,我们是通过spring.factories让starer项目中的Bean注入到Web模块的Spring容器中。本篇文章就来探究一下spring.factories文件,更深层次的东西,以及我们是如何借助该文件实例化Bean的。

2. 配置

spring.factories文件一般都是配置在class="lazy" data-src/main/resources/META-INF/ 目录下。

也就是说我们在IDEA新建的SpringBoot项目或者Maven项目的资源文件resources目录下新建一个META-INF文件夹,再建一个spring.factories文件即可,新建的文件没有问题的化,一般IDEA都能自动识别,如下图所示。

spring.factories 的文件内容就是接口对应其实现类,实现类可以有多个

文件内容必须是kv形式,即properties类型

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zhj.config.AutoConfiguration

如其一个接口有多个实现,如下配置:

org.springframework.boot.logging.LoggingSystemFactory=\
org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
org.springframework.boot.logging.java.JavaLoggingSystem.Factory

3. 原理

spring -core 中定义了SpringFactoriesLoader 类,这个类就是让spring.factories文件发挥作用的类。SpringFactoriesLoader类的作用就是检索META-INF/spring.factories文件,并获取指定接口将其实现实例化。 在这个类中定义了两个对外的方法:

  • loadFactories 根据给定的接口类获取其实现类的实例,这个方法返回的是对象列表
  • loadFactoryNames 根据给定的类型加载类路径的全限定类名,这个方法返回的是全限定类名的列表。

源码如下:

public final class SpringFactoriesLoader {
    
    // 文件位置
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    // 缓存
    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<T> result = new ArrayList<>(factoryImplementationNames.size());
        for (String factoryImplementationName : factoryImplementationNames) {
            // 实例化Bean,并将Bean放入到List集合中
            result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
        }
        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);
        }
    }
​
}

4. 总结

Spring通过SpringFactoriesLoader实例化Bean的过程

  • 获取SpringFactoriesLoader对应的类加载器
  • 查找缓存,查看缓存中是否已经读取到所有jar中classpath路径下的META-INF/spring.factories的内容
  • 如果缓存已经存在,根据/spring.factories文件中配置的全限定类名通过反射实例化Bean
  • 如果缓存中没有值,则扫描所有jar中的这个META-INF/spring.factories文件,并将其以读取到缓存中,并返回这个配置列表
  • 然后根据这个全限定类名的列表再通过反射实例化Bean

到此这篇关于SpringBoot借助spring.factories文件跨模块实例化Bean的文章就介绍到这了,更多相关SpringBoot实例化Bean内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

SpringBoot借助spring.factories文件跨模块实例化Bean

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录