java SPI

SPI是什么

SPI的英文名称是Service Provider Interface,是Java 内置的服务发现机制。

在开发过程中,将问题进抽象成API,可以为API提供各种实现。如果现在需要对API提供一种新的实现,我们可以不用修改原来的代码,直接生成新的Jar包,在包里提供API的新实现。通过Java的SPI机制,可以实现了框架的动态扩展,让第三方的实现能像插件一样嵌入到系统中。

Java的SPI类似于IOC的功能,将装配的控制权移到了程序之外,实现在模块装配的时候不用在程序中动态指明。所以SPI的核心思想就是解耦,这在模块化设计中尤其重要。

SPI实现步骤

1、写一个接口定义一些方法。如下

SPIService 接口 :

package com.example.orm.modules.spi;

/**
 * @description:
 * @create: 2020-08-03 09:25
 **/
public interface SPIService {
    void test();
}

实现类:

package com.example.orm.modules.spi;

/**
 * @description:
 * @create: 2020-08-03 09:26
 **/
public class SPIServiceImpl implements SPIService {
    @Override
    public void test() {
        System.out.println("hello i'm from SPIServiceImpl");
    }
}

 

package com.example.orm.modules.spi;

/**
 * @description:
 * @create: 2020-08-03 09:27
 **/
public class SPIServiceImpl2 implements SPIService {
    @Override
    public void test() {
        System.out.println("hello i'm from SPIServiceImpl2");
    }
}

2、实现该接口。

3、在src/main/java/resource 目录下创建META-INF/services目录,并以上述接口全限定名为名称创建文件。文件中写入实现类的全限定名。如下:

4、测试:

ServiceLoader<SPIService> spiServices = ServiceLoader.load(SPIService.class);
        Iterator<SPIService> iterator = spiServices.iterator();
        while(iterator.hasNext()) {
            SPIService next = iterator.next();
            next.test();
        }

结果:

hello i'm from SPIServiceImpl
hello i'm from SPIServiceImpl2



Process finished with exit code 0

应用场景

比较常见的应用场景:
JDK提供一个数据库驱动接口类,JDBC加载不同的数据库驱动实现类

日志门面接口实现类加载,SLF4J加载不同厂商提供的日志实现类。

Druid 中大量使用了SPI,但是对它做出了延伸扩展。利用filterChain配置扩展。

优缺点

优点:
使用Java SPI机制的优势是实现解耦,使得第三方服务模块的装配控制的逻辑与调用者的业务代码分离,而不是耦合在一起。应用程序可以根据实际业务情况启用框架扩展或替换框架组件。

相比使用提供接口jar包,供第三方服务模块实现接口的方式,SPI的方式使得源框架,不必关心接口的实现类的路径,可以不用通过下面的方式获取接口实现类:

  • 代码硬编码import 导入实现类
  • 指定类全路径反射获取:例如在JDBC4.0之前,JDBC中获取数据库驱动类需要通过Class.forName("com.mysql.jdbc.Driver"),类似语句先动态加载数据库相关的驱动,然后再进行获取连接等的操作
  • 第三方服务模块把接口实现类实例注册到指定地方,源框架从该处访问实例

通过SPI的方式,第三方服务模块实现接口后,在第三方的项目代码的META-INF/services目录下的配置文件指定实现类的全路径名,源码框架即可找到实现类

缺点:

  • 虽然ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。
  • 多个并发多线程使用ServiceLoader类的实例是不安全的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值