• Java SPI(Service Provider Interface)


    Java SPI是Java标准库提供的一种服务发现机制,它通过在classpath下约定的META-INF/services目录中,定义接口和其实现类之间的对应关系,从而动态加载目标接口的实现类。

    通过一个实际例子来具体看一下
    1、定义接口

    public interface Animal {
        void sayHello();
    }
    
    • 1
    • 2
    • 3

    2、实现类
    这里搞两个实现类

    public class Cat implements Animal {
        @Override
        public void sayHello() {
            System.out.println("喵喵");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class Dog  implements Animal{
        @Override
        public void sayHello() {
            System.out.println("汪汪");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、添加配置文件
    在resources目录下创建文件夹META-INF/services。
    然后在该目录下创建一个文件,以接口的全包名为文件名
    如我们这里的Animal接口就要创建
    com.test.Animal文件,然后文件内容为实现类全路径名:
    这里是上面两个Dog和Cat实现类

    com.test.Cat
    com.test.Dog
    
    • 1
    • 2

    一般情况下是要将当前工程打包成jar包方式供其它三方进行引用,这里为了简单就只在同一个工程下演示了。

    4、接口调用
    调用通过JDK提供的工具类ServiceLoader来完成

            ServiceLoader<Animal> serviceLoader = ServiceLoader.load(Animal.class);
            serviceLoader.forEach(Animal::sayHello);
    
    • 1
    • 2

    这里最后Cat和Dog两个实现类的sayHello方法都会被调到。

    5、原理分析
    读取就不用说了,就是根据接口全名读取其对应的配置文件。然后加载类是通过Class.forName方式

           private S nextService() {
                if (!hasNextService())
                    throw new NoSuchElementException();
                String cn = nextName;
                nextName = null;
                Class<?> c = null;
                try {//加载class
                    c = Class.forName(cn, false, loader);
                } catch (ClassNotFoundException x) {
                    fail(service,
                         "Provider " + cn + " not found");
                }
                if (!service.isAssignableFrom(c)) {
                    fail(service,
                         "Provider " + cn  + " not a subtype");
                }
                try {//实例化
                    S p = service.cast(c.newInstance());
                    providers.put(cn, p);
                    return p;
                } 
                
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这里看到是调用无参构造函数进行实例化。

    使用Java SPI 可以在不引入任何三方框架前提下实现解耦,接口的定义与具体业务实现分离开来。并且在不改变原逻辑情况下,通过修改配置实现类来修改实现。
    但是SPI也有一定的缺陷,不能按需加载只能全量扫描加载。例如上面我们配置了两个实现类,一次都会扫描加载。也不够灵活,不能通过一些条件去更精确的加载自己想要的服务实现类。

  • 相关阅读:
    软件盘点企业使用服装ERP的好处
    适合Linux新手使用的版本有哪些?
    数据结构之八大排序——简单选择排序
    vue-danmak 弹幕交互组件
    基于极限学习机 (ELM) 进行正弦波预测(Matlab代码实现)
    Kafka (七) --------- Kafka-Eagle 监控
    dmesg和addr2line命令定位段错误
    HDU 1712:ACboy needs your help ← 分组背包问题
    某网站获取到正文内容
    数据结构 - 2(顺序表10000字详解)
  • 原文地址:https://blog.csdn.net/sinat_16493273/article/details/133812313