spring.factories 文件配置详情

这篇具有很好参考价值的文章主要介绍了spring.factories 文件配置详情。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用场景

在程序开发中,可能会出现包名不一样的情况(如:pom 依赖的很多的 jar),如何解决Spring Boot不能被默认路径扫描呢?

  • 方法一:在 Spring Boot Application 主类上使用 @Import 注解。
  • 方法二:使用 spring.factories 文件

方法一比较简单,在此就不做过多介绍,主要谈谈 spring.factories 使用。

作用

spring.factories 的作用是使用外部 jar 时不用再写配置,会自动加入已经配置好的配置。

内部原理机制

spring.factories 这种机制实际上是仿照 java 中的 SPI 扩展机制实现的。

SPI机制

SPI全称Service Provider Interface,是 Java 提供的一套用来被第三方实现或者扩展的接口,其意义在于为某个接口寻找服务的实现,主要应用在框架中用来寻找组件,提高扩展性。

面向的对象设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及了具体的实现类,就违反了可插拔的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

Java 中的 SPI 机制:为某个接口寻找服务的实现的机制,有点类似 IOC 的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制很重要。

Spring Boot 中的 SPI 机制:在 META-INFO/spring.factories 文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。这种自定义的 SPI 机制就是 Spring Boot Starter 实现的基础。

Spring Factories 实现原理

spring.factories 实现是依赖 spring-core 包里的 SpringFactoriesLoader 类,这个类实现了检索 META-INF/spring.factories 文件,并获取指定接口的配置的功能。

这个类中定义了两个对外的方法:

  • loadFactories:根据接口类获取其实现类的实例,这个方法返回的是对象列表
  • loadFactoryNames:根据接口获取其接口类的名称,这个方法返回的是类名的列表

上面两个方法的关键都是从指定的 ClassLoader 中获取 spring.factories 文件,并解析得到类名列表,具体代码如下:

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
    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 (classLoader == 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());
        Iterator var5 = factoryImplementationNames.iterator();

        while(var5.hasNext()) {
            String factoryImplementationName = (String)var5.next();
            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();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

    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() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(factoryImplementationClass, new Class[0]).newInstance();
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]", var4);
        }
    }
}

从上述代码中可以看到,在这个方法中会遍历整个 ClassLoader 中所有 jar 包下的 spring.factories 文件,文件之间不会相互影响配置,也不回被别人的配置覆盖。

spring.factories 的是通过 Properties 解析得到的,所以在写文件中的内容都是按照下面这种方式配置的。

用法及配置

spring.factories 文件必须放在 resources 目录下的 META-INF 的目录下,否则不会生效。如果一个接口希望配置多个实现类,可以用","分割。

spring.factories 各种配置的具体含义:

ApplicationContextInitializer

该配置项用来配置实现了 ApplicationContextInitializer 接口的类,这些类用来实现上下文初始化。配置如下:

org.springframework.context.ApplicationContextInitializer=\
com.xh.config.MyApplicationContextInitializer
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
 
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("MyApplicationContextInitializer.initialize() " + applicationContext);
    }
 
}

ApplicationListener

配置应用程序监听器,该监听器必须实现 ApplicationListener 接口。它可以用来监听 ApplicationEvent 事件。配置如下:

org.springframework.context.ApplicationListener=\
com.xh.factories.listener.EmailApplicationListener
@Slf4j
public class EmailApplicationListener implements ApplicationListener<EmailMessageEvent> {

    @Override
    public void onApplicationEvent(EmailMessageEvent event) {
        log.info("模拟发送邮件... ");
        log.info("EmailApplicationListener 接受到的消息:{}", event.getContent());
    }
}

AutoConfigurationImportListener

该配置项用来配置自动配置导入监听器,监听器必须实现 AutoConfigurationImportListener  接口。该监听器可以监听 AutoConfigurationImportEvent 事件。配置如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
com.xh.config.MyAutoConfigurationImportListener
public class MyAutoConfigurationImportListener implements AutoConfigurationImportListener {
 
    @Override
    public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
        System.out.println("MyAutoConfigurationImportListener.onAutoConfigurationImportEvent() " + event);
    }
 
}

AutoConfigurationImportFilter

配置自动配置导入过滤器,过滤器必须实现 AutoConfigurationImportFilter 接口。该过滤器用来过滤那些自动配置类可用。配置如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
com.xh.config.MyConfigurationCondition
public class MyConfigurationCondition implements AutoConfigurationImportFilter {
 
    @Override
    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
        System.out.println("MyConfigurationCondition.match() autoConfigurationClasses=" +  Arrays.toString(autoConfigurationClasses) + ", autoConfigurationMetadata=" + autoConfigurationMetadata);
        return new boolean[0];
    }
 
}

EnableAutoConfiguration

配置自动配置类。这些配置类需要添加 @Configuration 注解。配置如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xh.config.MyConfiguration
@Configuration
public class MyConfiguration {
 
    public MyConfiguration() {
        System.out.println("MyConfiguration()");
    }
 
}

FailureAnalyzer

配置自定的错误分析类,该分析器需要实现 FailureAnalyzer 接口。配置如下:

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.huangx.springboot.autoconfig.MyFailureAnalyzer
/**
 * 自定义错误分析器
 */
public class MyFailureAnalyzer implements FailureAnalyzer {
 
    @Override
    public FailureAnalysis analyze(Throwable failure) {
        System.out.println("MyFailureAnalyzer.analyze() failure=" + failure);
        return new FailureAnalysis("MyFailureAnalyzer execute", "test spring.factories", failure);
    }
 
}

TemplateAvailabilityProvider

配置模板的可用性提供者,提供者需要实现 TemplateAvailabilityProvider 接口,配置如下:文章来源地址https://www.toymoban.com/news/detail-429470.html

org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
com.huangx.springboot.autoconfig.MyTemplateAvailabilityProvider
/**
 * 验证指定的模板是否支持
 */
public class MyTemplateAvailabilityProvider implements TemplateAvailabilityProvider {
 
    @Override
    public boolean isTemplateAvailable(String view, Environment environment, ClassLoader classLoader, ResourceLoader resourceLoader) {
        System.out.println("MyTemplateAvailabilityProvider.isTemplateAvailable() view=" + view + ", environment=" + environment + ", classLoader=" + classLoader + "resourceLoader=" + resourceLoader);
        return false;
    }
 
}

到了这里,关于spring.factories 文件配置详情的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot中配置文件介绍及其使用教程

    目录 一、配置文件介绍 二、配置简单数据 三、配置对象数据 四、配置集合数据 五、读取配置文件数据 六、占位符的使用 SpringBoot项目中,大部分配置都有默认值,但如果想替换默认配置的话,就可以使用application.properties或者application.yml或者application.yaml进行配置。SpringBo

    2024年02月08日
    浏览(37)
  • 如何在Spring Boot中使用外部配置文件?

    在Spring Boot中,可以使用外部配置文件来配置应用程序的行为。外部配置文件通常包含敏感信息,例如数据库凭据或安全令牌,以及一些通用配置,例如端口号、日志级别等。 要在Spring Boot中使用外部配置文件,请按照以下步骤操作: 1、创建配置文件 首先,创建一个名为

    2024年02月09日
    浏览(29)
  • 【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理

    💓 博客主页:从零开始的-CodeNinja之路 ⏩ 收录文章:【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理 🎉欢迎大家点赞👍评论📝收藏⭐文章 配置文件主要是为了解决硬编码带来的问题,把可能会发生改变的信息,放在⼀个集中的地方,当我们启 动某个程序时,应用程

    2024年04月23日
    浏览(29)
  • 电商平台商品详情接口的应用场景

    价格、库存量、发货地点等。此外,它还可以提供商品的详细信息,包括商品的图片、详细描述、规格参数、售后服务等。这些信息可以帮助用户更好地了解商品,从而更好地选择商品。 其次,电商平台商品详情接口的实现原理是基于RESTful API。RESTful API是一种基于HTTP协议的

    2023年04月12日
    浏览(33)
  • 【Spring Boot】使用XML配置文件实现数据库操作(一)

    SQL映射文件就是我们通常说的mapper.xml配置文件,主要实现SQL语句的配置和映射,同时实现Java的POJO对象与数据库中的表和字段进行映射关联的功能。 1.1 mapper.xml的结构 下面就来详细介绍mapper.xml文件的结构。首先看一个完整的mapper.xml示例:

    2024年02月10日
    浏览(45)
  • Spring02-Spring注解的使用、基于注解的IOC、纯注解配置、整合Junit、AOP入门、基于配置文件的AOP、切入点表达式、基于配置的文件环绕通知

    学习基于注解的 IOC 配置,即注解配置 和 XML 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。 关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯 , 所以这两种配置方式我们都需要掌握。 把 Spring 的 xml 配置内容改为使用

    2024年02月03日
    浏览(63)
  • 场景烘培分享——详情unity手册

    1.灯光介绍 Unity灯光的简介 Area Light 区域光(只能用做烘培,常用雨补光或者是发光的屏幕上) spot light 射灯(和点光源搭配使用对于路灯的使用比较多) point点光源(对于发光的物体使用较频繁) Drictor light平行光(就是太阳光)一个场景里面至少有一个主灯光来照射。 2

    2023年04月12日
    浏览(27)
  • Nginx在Window与Mac环境的使用及配置详情

    什么是nginx? Nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。 由俄罗斯的程序设计师Igor Sysoev所开发,官方测试nginx能够支支撑5万并发链接, 并且cpu、内存等资源消耗却非常低,运行非常稳定。 应用场景 http服务器。Nginx是一个http服务可以独

    2024年02月04日
    浏览(29)
  • 在Spring Boot微服务使用jasypt-spring-boot加密和解密yml配置文件

    记录 :424 场景 :在Spring Boot微服务,使用jasypt-spring-boot加密和解密yml配置文件中的配置信息。 版本 :JDK 1.8,Spring Boot 2.6.3,jasypt-1.9.3,jasypt-spring-boot-2.1.2, jasypt-spring-boot-3.0.5。 开源地址 :https://github.com/ulisesbocchio/jasypt-spring-boot 1.在Spring Boot微服务使用jasypt-spring-boot-3.0.5版本

    2024年02月09日
    浏览(59)
  • 【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】

    Spring 框架可以说是 Java 开发中最重要的框架,功能 非常 强大 中文文档:https://springdoc.cn/spring/ 官网:https://spring.io/ Spring makes Java Simple、modern、productive … Spring 框架的几个核心概念: IoC: I nversion o f C ontrol:控制反转 DI: D ependency I njection:依赖注入 AOP: A spect O riented P rogram

    2024年02月09日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包