首页 > 其他分享 >SPI机制

SPI机制

时间:2023-05-12 16:13:11浏览次数:34  
标签:SpiInterface 实现 接口 SPI META 机制 INF

SPI机制

SPI (Service Provider Interface),主要适用于扩展作用,SPI可以很灵活的让接口和实现分离,让api提供者只提供接口,第三方来实现,然后可以使用配置文件的方式来实现替换或者扩展,在框架中比较常见,提高框架的可扩展性。

JDK的SPI机制-ServiceLoader

ServiceLoader是Java提供的一种简单的SPI机制的实现,Java的SPI实现约定了以下两件事:

  • 文件必须放在META-INF/services/目录底下
  • 文件名必须为接口的全限定名,内容为接口实现的全限定名

案例实现:

  • 定义接口
public interface SpiInterface {
    String spi()
}
  • 实现接口
public class SpiInterfaceTest1 implements SpiInterface {
    @Override
    public String spi() {
        return "SPI实现类test1";
    }
}
public class SpiInterfaceTest2 implements SpiInterface {
    @Override
    public String spi() {
        return "SPI实现类TEST2";
    }
}
  • 在resources目录下,建立文件夹META-INF/services,创建一个以接口的全限定名为名称的文件,内容是该实现类的全限定名

在这里插入图片描述
在这里插入图片描述

  • 使用ServiceLoader.load()方法来加载实现类
    public static void main(String[] args) {
        /*JDK SPI*/
        ServiceLoader<SpiInterface> load = ServiceLoader.load(SpiInterface.class);
        Iterator<SpiInterface> iterator = load.iterator();
        while (iterator.hasNext()) {
            SpiInterface spiInterface = iterator.next();
            System.out.println(spiInterface.spi());
        }
    }
测试结果:
SPI实现类test1
SPI实现类TEST2

java中最常见的spi机制应用就是数据库驱动的加载,java其实就是定义了java语言跟数据库交互的接口,但是具体的实现得交给各大数据库厂商来实现,那么java怎么知道你的数据库厂商的实现了?这时就需要spi机制了,java好约了定在 Classpath 路径下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,然后内容是该数据库厂商的实现的接口的全限定名,这样数据库厂商只要按照这个规则去配置,java就能找到。

缺点:

  • JDK的SPI会把所有的对象都实例化到内存中,不管你用不用,可能会导致资源的浪费
  • 无法区分使用者应该使用哪一个实现类,需要从接口设计上入手决定使用那个实现类,不够灵活

Spring的SPI机制-SpringFactoriesLoader

Spring的SPI机制的约定如下:

  • 配置文件必须在META-INF/目录下,文件名必须为spring.factories
  • 文件内容为键值对,一个键可以有多个值,只需要用逗号分割就行,同时键值都需要是类的全限定名,键和值可以没有任何类与类之间的关系,当然也可以有实现的关系。

案例实现:

  • 配置spring.factories

在Spring中提供的SPI机制,我们只需要在 META-INF/spring.factories 中配置接口实现类名,即可通过服务发现机制,在运行时加载接口的实现类:

在这里插入图片描述

  • 使用SpringFactoriesLoader.loadFactories()来加载
        List<SpiInterface> spiInterfaces = SpringFactoriesLoader.loadFactories(SpiInterface.class, Demo.class.getClassLoader());
        for (SpiInterface spiInterface : spiInterfaces) {
            System.out.println(spiInterface.spi());
        }
测试结果:
SPI实现类test1
SPI实现类TEST2
  • 自动配置原理

在我们日常定义starter的时候,一般在spring.factories里会定义成org.springframework.boot.autoconfigure.EnableAutoConfiguration作为key,这样是为了SpringBoot启动的时候,根据@EnableAutoConfiguration注解来自动注入配置

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

在引入EnableAutoConfiguration的时候,会执行AutoConfigurationImportSelector里的逻辑,而真正去扫描各类starter的是其中的一个继承类ImportAutoConfigurationImportSelector

	protected Collection<String> loadFactoryNames(Class<?> source) {
		List<String> factoryNames = new ArrayList<>(
				SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader()));
		ImportCandidates.load(source, getBeanClassLoader()).forEach(factoryNames::add);
		return factoryNames;
	}

SpringFactoriesLoader.loadFactoryNames:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);

这里就用到了SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader())去扫描各starter下的META-INF/spring.factories配置文件,然后通过反射等操作把文件里的value值变成Spring里的Bean对象,完成自动配置;

但是SpringBoot3.0之后不再使用SpringFactoriesLoader,而是Spring重新从META-INF/spring/目录下的org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中读取了。

优点:

  • 不需要像JDK的SPI,一个接口就要定义一个文件,而是有统一的spring.factories配置文件,文件内容由k-v组成
  • Spring的SPI机制提供了获取类限定名的方法loadFactoryNames,而Java的SPI机制是没有的。通过这个方法获取到类限定名之后就可以将这些类注入到Spring容器中,用Spring容器加载这些Bean,而不仅仅是通过反射

缺点:

  • 没有实现获取指定某个指定实现类的功能,所以要想能够找到具体的某个实现类,还得依靠具体接口的设计

Dubbo的SPI机制-ExtensionLoader

Dubbo的SPI机制也做了以下几点约定:

  • 接口必须要加@SPI注解
  • 配置文件可以放在META-INF/services/META-INF/dubbo/internal/ META-INF/dubbo/META-INF/dubbo/external/这四个目录底下,文件名也是接口的全限定名,一般使用META-INF/dubbo/
  • 内容为键值对,键为短名称(可以理解为spring中Bean的名称),值为实现类的全限定名

案例实现:

在这里插入图片描述

测试:

        ExtensionLoader<SpiInterface> extensionLoader =
                ApplicationModel.defaultModel()
                        .getExtensionDirector()
                        .getExtensionLoader(SpiInterface.class);
        SpiInterface test1 = extensionLoader.getExtension("test1");
        System.out.println(test1.spi());

Dubbo的SPI是可以根据指定的key来选择使用哪个实现类的,这样比较灵活

dubbo核心机制

  • 自适应机制

自适应机制说的是在代码运行过程中,可以基于参数动态的选择到具体的实现类来执行

每个接口有且只有一个自适应类,可以通过extensionLoader.getAdaptiveExtension();得到这个自适应类

自适应类有两种方式产生,第一种就是自己指定,就是上面测试的那种写法,执行要执行的key

另外一种是在接口的实现类上加@Adaptive注解,那么这个这个实现类就是自适应实现类。

@Adaptive
public class SpiInterfaceTest1 implements SpiInterface {
    @Override
    public String spi() {
        return "SPI实现类test1";
    }
}
        ExtensionLoader<SpiInterface> extensionLoader =
                ApplicationModel.defaultModel()
                        .getExtensionDirector()
                        .getExtensionLoader(SpiInterface.class);
        SpiInterface adaptiveExtension = extensionLoader.getAdaptiveExtension();
        System.out.println(adaptiveExtension.spi());

// SPI实现类test1
  • 自动激活

自动激活,就是根据你的参数,动态的选择一批实现类返回给你,最常见的使用方式就是dubbo里Filter,可以动态的选择是提供者还是消费者

自动激活的实现类上需要加上Activate注解:

@Activate(group = {CommonConstants.PROVIDER})
public class DubboRequestFilter implements Filter {

这就说明该DubboRequestFilter在dubbo的提供方会被激活,提供自定义拦截器作用
在这里插入图片描述

标签:SpiInterface,实现,接口,SPI,META,机制,INF
From: https://www.cnblogs.com/sun2020/p/17394478.html

相关文章

  • SPI方式读写SD卡速度有多快?
    SD卡是一个嵌入式中非常常用的外设,可以用于存储一些大容量的数据。但是,用单片机读写SD卡速度一般都有限(对于高速SD卡,主要是受限于单片机本身的接口速度),在高速、实时数据存储时可能会有影响。那么,具体速度可以达到多少呢?让我们来实际测试一下。SD卡一般有两种常用的接口SPI和SDIO,S......
  • 易基因:DNA甲基化和转录组分析揭示野生草莓干旱胁迫分子调控机制|植物抗逆
    大家好,这里专注表观组学十余年,领跑多组学科研服务的易基因。干旱胁迫是对农业生产产生不利影响的关键环境因素。为此,植物发展出各种响应机制(干旱逃逸、避免、耐受和回复),以通过进化增强抗旱性,这些适应机制从分子到植物水平都所不同。黄毛草莓(Fragarianilgerrensis)是一种具有良好......
  • 通过 UE的反射机制来查找类里面的所有UPROPERTY,UFUCTION
    Property涵盖了UE中所有的原生类型Property是以链表的形式存储的,UE中提供了两种遍历方式:TFieldIteratorFPropertyValueIterator示例: UObject*Content;for(TFieldIterator<FProperty>PropertyIter(Content->GetClass());PropertyIter;++PropertyIter) { FPro......
  • SpringBoot3.x中spring.factories SPI 服务发现机制的改变
    目录一、基础背景二、服务发现接口spring.factories三、服务发现机制调用1.启动SpringApplication2.加载SpringApplication.run1.SpringApplication.createApplicationContext2.SpringApplication.prepareContext3.SpringApplication.refreshContext4.AutoConfigurationImportSele......
  • web游览器的标签页仿 ios mac 苹果的墓碑机制 (js代码)
    背景:本来项目开发系统防挂机功能,在其余游览器中均可以使用。但是呢在苹果的safair游览器中会出现几率失效,最后经过排查发现是苹果的墓碑机制导致。即:此标签页活跃,其他标签页假死。然后就导致防挂机失效了。原理:假如当前游览器中有3个标签页分别是A,B,C,每个标签页都有倒计时。正......
  • oracle 开启tnsping trace、sqlnet trace 、event10257
    在sqlnet.ora文件中加入以下参数:TNSPING.TRACE_LEVEL=SUPPORTTNSPING.TRACE_DIRECTORY=d:\oracle\trace“tnsping”工具的预期用途仅仅是测试OracleNet别名中指定的数据库侦听器是up还是down。“tnsping”工具不打算用作OracleNet性能测量工具B.Sql*nettraceSE......
  • Android spinner 的使用
    1.设置下拉框的值1.1通过xml  entries属性配置需要将数据写在xml中,然后设置下拉框的entries属性,则数据自动加载到下拉框中。具体如下: layout布局引用<Spinnerandroid:layout_weight="1.5"android:id="@+id/sp_bound_rate"......
  • zookeeper选举机制
    1.zookeeper选举机制zookeeper默认算法是FastLeaderElection,采用投票数大于半数则胜出的逻辑2.概念2.1)服务器ID比如有三台服务器,编号分别是1,2,3。编号越大在选择算法中的权重越大。2.2)选举状态Looking,竞选状态。Following,随从状态,同步leader状态,参与投票。Obeserving,观察......
  • IO通信机制
    1.IO通信模型IO指的是input和output网络通信的本质是网络间的数据IO。只要有IO,就会有阻塞或非阻塞的问题,无论这个IO是网络的,还是硬盘的。原因在于程序是运行在系统上的,任何形式的IO操作都需要系统支持。2.BIO(阻塞模式)BIO即BlockingIO,是一种阻塞式的IO。jdk1.4版本之前的......
  • socket机制
    1.概述:Socket,又称为套接字,用于描述IP地址和端口。应用程序通常通过socket向网络发出请求或者应答网络请求。Socket就是网络编程提供的一种机制:通信两端都有socket;网络通信其实就是socket之间的通信;数据在两个socket之间通过io传输。网络编程也称作为socket编程,套接字编程。......