Spring中的spring.factories文件用法(Spring如何加载第三方Bean)

目录
  • Spring的spring.factories文件用法
    • 问题
    • 解决
  • SpringBoot的扩展机制之Spring Factories
    • 什么是 SPI机制
    • Spring Boot中的SPI机制
    • Spring Factories实现原理是什么
    • Spring Factories在Spring Boot中的应用

Spring的spring.factories文件用法

在springBoot中,它自动扫描包的时候,只会扫描自己模块下的类。

问题

如果我们不想被Spring容器管理的Bean的路径下不再SpringBoot的包扫描路径下,怎么办呢?如何加载别的第三方Bean呢?

解决

首先我们创建一个工程,另外创建一个与启动类不在一个级别的目录。

第一种方法就是使用在启动类上加上@Import注解。

@Import(value = {Test.class})

第二种方法就是创建spring.factories文件

现在我们将其改造一下,采用spring.factories的方式去加载Test类,在resources目录下新建一个META-INF的目录,然后再新建一个spring.factories文件,文件内容为:

下面第二条就是我们自己的类的路径。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=/
com.huawei.it.config.Test

然后在springBoot中的启动类中将@Import注释掉,启动一下,在控制台上就会发现,我们自己的配置类已经加载到Spring容器中去了,所以Spring可以加载一个工程下的任意一下工程类了。

应用

下面就是我在Nacos源码中看到的,可以看到spring.factories文件中内容,与我们自己类加载到Spring容器中是一样的道理。

SpringBoot的扩展机制之Spring Factories

写在前面:Spring Boot中有一种非常解耦的扩展机制:Spring Factories。这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的。

什么是 SPI机制

SPI的全名为Service Provider Interface.大多数开发人员可能不熟悉,因为这个是针对厂商或者插件的。在java.util.ServiceLoader的文档里有比较详细的介绍。

简单的总结下java SPI机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

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

Spring Boot中的SPI机制

在Spring中也有一种类似与Java SPI的加载机制。它在META-INF/spring.factories文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。

这种自定义的SPI机制是Spring Boot Starter实现的基础。

Spring Factories实现原理是什么

spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。在这个类中定义了两个对外的方法:

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

loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。

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

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
        Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        List<String> result = new ArrayList<String>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。也就是说我们可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖。

spring.factories的是通过Properties解析得到的,所以我们在写文件中的内容都是安装下面这种方式配置的:

com.xxx.interface=com.xxx.classname

如果一个接口希望配置多个实现类,可以使用','进行分割。

Spring Factories在Spring Boot中的应用

在Spring Boot的很多包中都能够找到spring.factories文件,接下来我们以spring-boot包为例进行介绍

在日常工作中,我们可能需要实现一些SDK或者Spring Boot Starter给被人使用时, 我们就可以使用Factories机制。Factories机制可以让SDK或者Starter的使用只需要很少或者不需要进行配置,只需要在服务中引入我们的jar包即可。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring容器中添加bean的5种方式

    目录 @Configuration + @Bean @Componet + @ComponentScan @Import注解导入 @Import直接导入类 @Import + ImportSelector @Import + ImportBeanDefinitionRegistrar @Import + DeferredImportSelector 使用FactoryBean接口 使用 BeanDefinitionRegistryPostProcessor 小结 我们知道平时在开发中使用Spri

  • 如何正确控制springboot中bean的加载顺序小结篇

    1.为什么需要控制加载顺序 springboot遵从约定大于配置的原则,极大程度的解决了配置繁琐的问题.在此基础上,又提供了spi机制,用spring.factories可以完成一个小组件的自动装配功能. 在一般业务场景,可能你不大关心一个bean是如何被注册进spring容器的.只需要把需要注册进容器的bean声明为@Component即可,spring会自动扫描到这个Bean完成初始化并加载到spring上下文容器. 而当你在项目启动时需要提前做一个业务的初始化工作时,或者你正在开发某个中间

  • 详解Spring简单容器中的Bean基本加载过程

    本篇将对定义在 XMl 文件中的 bean,从静态的的定义到变成可以使用的对象的过程,即 bean 的加载和获取的过程进行一个整体的了解,不去深究,点到为止,只求对 Spring IOC 的实现过程有一个整体的感知,具体实现细节留到后面用针对性的篇章进行讲解. 首先我们来引入一个 Spring 入门使用示例,假设我们现在定义了一个类 org.zhenchao.framework.MyBean ,我们希望利用 Spring 来管理类对象,这里我们利用 Spring 经典的 XMl 配置文件形式进行

  • 详解Spring中Bean的加载的方法

    之前写过bean的解析,这篇来讲讲bean的加载,加载要比bean的解析复杂些,从之前的例子开始. Spring中加载一个bean的方式: TestBean bean = factory.getBean("testBean"); 来看看getBean(String name)方法源码, @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, nul

  • Spring中的spring.factories文件用法(Spring如何加载第三方Bean)

    目录 Spring的spring.factories文件用法 问题 解决 SpringBoot的扩展机制之Spring Factories 什么是 SPI机制 Spring Boot中的SPI机制 Spring Factories实现原理是什么 Spring Factories在Spring Boot中的应用 Spring的spring.factories文件用法 在springBoot中,它自动扫描包的时候,只会扫描自己模块下的类. 问题 如果我们不想被Spring容器管理的Bean的路径下不

  • 浅谈spring中isolation和propagation的用法

    可以在XML文件中进行配置,下面的代码是个示意代码 <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED"/>增加记录的方法 <t

  • 详解Spring中@Valid和@Validated注解用法

    目录 案例引入 @Valid 详解 @Validated 详解 @Valid 和 @Validated 比较 案例引入 下面我们以新增一个员工为功能切入点,以常规写法为背景,慢慢烘托出 @Valid 和 @Validated 注解用法详解. 那么,首先,我们会有一个员工对象 Employee,如下 : /** * 员工对象 * * @author sunnyzyq * @since 2019/12/13 */ public class Employee { /** 姓名 */ public St

  • C++中TinyXML读取xml文件用法详解

    目录 前言 XML文件理解 常用的XML类方法使用 总结 前言 TinyXML下载地址:https://sourceforge.net/projects/tinyxml/ 官方文档:TinyXML TinyXML是个解析库,它由两个头文件(.h文件)和四个CPP文件(.cpp文件)构成,用的时候,只要将(tinyxml.h.tinystr.h.tinystr.cpp.tinyxml.cpp.tinyxmlerror.cpp.tinyxmlparser.cpp)导入工程就可以用它的东西了.如果需要

  • JS中使用gulp实现压缩文件及浏览器热加载功能

    gulp类似于grunt,都是基于Node.js的前端构建工具.不过gulp压缩效率更高. 一.安装gulp 首先,你要安装过nodejs,如果没有安装过的同学请自行下载.  先再命令行里输入   npm install gulp -g   下载gulp 二.创建gulp项目 创建一个你需要项目文件夹,然后在根目录输入  npm init  (npm init命令会为你创建一个package.json文件,这个文件保存着这个项目相关信息.比如你用到的各种依赖) 三.使用npm install 安

  • Yii安装EClientScript插件扩展实现css,js文件代码压缩合并加载功能

    本文实例讲述了Yii安装EClientScript插件扩展实现css,js文件代码压缩合并加载功能.分享给大家供大家参考,具体如下: 扩展插件下载地址,解压后复制到/protected/vendor/ https://github.com/muayyad-alsadi/yii-EClientScript main配置文件配置插件,components里面增加 //js,css代码压缩,合并 'clientScript' => array( 'class' => 'application.ven

  • Android中使用ScrollView实现滑动到底部显示加载更多

    这是效果 主要是onTouchListener监听事件,监视什么时候滑到底部 同时要理解getMeasuredHeight和getHeight的区别 getMeasuredHeight:全部的长度 包括隐藏的 getHeight:在布局中展示出来的长度 布局文件: <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_paren

  • vue单页面打包文件大?首次加载慢?nginx带你飞,从7.5M到1.3M蜕变过程(推荐)

    找到nginx多网站配置文件:类似 nginx/sites-available/www.baidu.com server { listen 80; index index.html index.htm index.nginx-debian.html; server_name www.baidu.com; location / { root /mnt/www/www.baidu.com; try_files $uri $uri/ /index.html; } } 参考页面:https://rout

  • 模拟jQuery中的ready方法及实现按需加载css,js实例代码

    一.ready函数的实现经常用jQuery类库或其他类库中的ready方法,有时候想想它们到底是怎么实现的,但是看了一下jQuery中的源码,涉及到的模块比较多,(水平有限)代码比较难看懂:自己结合了一些书籍内容,总结一下.先说一下ready函数的实现思路:变量ready通过表达式赋值,右侧为一个自执行匿名函数,在这个匿名函数中,首先为各个浏览器的事件绑定处理函数,并为isReady赋值(根据事件异步处理程序来确定),然后返回一个传参闭包,在闭包中,主要判断isReady值来执行操作,如果dom

  • jQuery中借助deferred来请求及判断AJAX加载的实例讲解

    ajax请求异步队列加载 我们在开发程序的时候通常会碰到使用ajax加载数据显示到列表的情况.ajax默认使用异步加载(async:true).为什么不使用同步呢,因为ajax同步加载会UI渲染线程阻塞的问题.通常表现为在加载大量数据时由于加载时间过长导致页面不能点击.gif动画卡死以及浏览器崩溃等问题.所以,一般情况下,尽量使用ajax异步加载. 可是,我们有些时候的需求要求ajax同步加载,一个加载完再加载下一个,即所谓的队列.前面我们有说过,同步加载会引起UI渲染阻塞问题.那么我们要怎么实

随机推荐