JAVASE-16 SPI

JAVASE-16 SPI

最近写业务代码的时候,遇到了SPI的概念,是个遗漏的知识点,总结一下

1.SPI概念

SPI 是英文Service Provider Interface的缩写, 即服务提供商接口。

以jdbc为例,在jdk中提供了jdbc的接口类,不同厂商分别提供对应的实现类,如:mysql-jdbc,oralce-jdbc,sqlserver-jdbc等。

JDBC的接口类是java的基础类库,是由引导类加载器加载进jvm的;实现类是在运行时从厂家的jar包中加载得到的,因此至少是通过系统类加载器加载进jvm内存。
这样路是堵死的,引导类加载器加载的类根本看不到系统类加载器加载的类,因此无法调用。此时需要使用到线程上下文类加载器

SPI(服务提供商接口),顾名思义:jdk提供规范,厂商实现。因此,它和线程上下文类加载器有着天然的联系。

线程上下文类加载器的介绍 可参考博客:JVM-3 类加载机制

2.规范

  1. 当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
  2. 接口实现类所在的jar包放在主程序的classpath中;
  3. 主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置 文件找到实现类的全限定名,把类加载到JVM;
  4. SPI的实现类必须携带一个不带参数的构造方法;

3.实例

代码目录情况如下:
在这里插入图片描述
提供一个接口CarSpi接口,以及3个实现类:AudiSpiImpl,BenciSpiImpl ,BmwSpiImpl
以及一个配置文件

package com.shengyu.service;

public interface CarSpi {
    void print();
}

实现类1:AudiSpiImpl

package com.shengyu.service.impl;

import com.shengyu.service.CarSpi;

public class AudiSpiImpl implements CarSpi {
    public void print() {
        System.out.println("------audi----");
    }
}

实现类2:BenciSpiImpl

package com.shengyu.service.impl;

import com.shengyu.service.CarSpi;

public class BenciSpiImpl implements CarSpi {
    public void print() {
        System.out.println("------benci----");
    }
}

实现类3:BmwSpiImpl

package com.shengyu.service.impl;

import com.shengyu.service.CarSpi;

public class BmwSpiImpl implements CarSpi {
    public void print() {
        System.out.println("------bmw----");
    }
}

配置文件:resources/META-INF/services文件夹下 com.shengyu.service.CarSpi文件

com.shengyu.service.impl.AudiSpiImpl
com.shengyu.service.impl.BenciSpiImpl
com.shengyu.service.impl.BmwSpiImpl

客户端代码

package com.shengyu;

import com.shengyu.service.CarSpi;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;

public class Application {
    public static void main(String[] args) {
        ServiceLoader serviceLoader = ServiceLoader.load(CarSpi.class, CarSpi.class.getClassLoader());
        Iterator<CarSpi> iterator = serviceLoader.iterator();
        List<CarSpi> carSet = new ArrayList<>();
        while (iterator.hasNext()) {
            carSet.add(iterator.next());
        }
        carSet.stream().forEach(carSpi -> {
            carSpi.print();
        });
    }
}

4.总结

1.SPI天生具有良好的扩展性。如果需要添加新的功能,直接引入CarSpi的实现类即可,并直接修改配置文件即可,客户端代码不需要编译,满足开闭原则;
2.SPI可以用来生成一组CarSpiI-mpl (表示CarSpi的实现类);

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值