• 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也有一定的缺陷,不能按需加载只能全量扫描加载。例如上面我们配置了两个实现类,一次都会扫描加载。也不够灵活,不能通过一些条件去更精确的加载自己想要的服务实现类。

  • 相关阅读:
    宏任务、微任务理解
    Node的知识理解
    怎么把手机ip地址变成了外省
    常见的近似算法
    Redis-列表
    获取鼠标在画布中的位置
    大厂设计师力推的14款平面图设计工具!
    Java中时间类Date和Calendar的基本知识
    基本计算(空军工程大学)
    postgresql14-用户与角色(二)
  • 原文地址:https://blog.csdn.net/sinat_16493273/article/details/133812313