广告
返回顶部
首页 > 资讯 > 后端开发 > Python >SpringBoot借助spring.factories文件跨模块实例化Bean
  • 412
分享到

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

2024-04-02 19:04:59 412人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录1. 前言2. 配置3. 原理4. 总结1. 前言 SpringBoot在包扫描时,并不会扫描子模块下的内容,这样就使得我们的子模块中的Bean无法注入到spring容器中。Sp

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文件一般都是配置在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内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: SpringBoot借助spring.factories文件跨模块实例化Bean

本文链接: https://www.lsjlt.com/news/147568.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作