JAVA 拾遗 -- 关于 SPI 机制

原文:https://www.cnkirito.moe/spi/

一、实现一个自定义的SPI

1.项目结构

  • invoker是用来测试的主项目
  • interface是针对厂商和插件商定义的接口项目,只提供接口,不提供实现
  • good-printer,bad-printer分别是两个厂商对interface的不同实现,所以他们会依赖于interface项目

主要实现的是,在不改变invoker代码,只更改依赖的前提下,切换interface的实现厂商

2.interface模块

com.spi.Printer

public interface Printer {
    void print();
}

interface只定义一个接口,不提供实现。规范的制定方一般都是比较牛叉的存在,这些接口通常位于java,javax前缀的包中,这里的printer只是模拟的一个规范接口。

3.good-printer模块
3.1 good-printer\pom.xml
<dependencies>
    <dependency>
        <groupId>com.lara</groupId>
        <artifactId>interface</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

规范的具体实现类必然要依赖规范接口

3.2 com.spi.GoodPrinter
public class GoodPrinter implements Printer {
    public void print() {
        System.out.println("you are good");
    }
}

作为printer规范接口的实现一

3.3 在resource下添加services文件

每一个SPI接口都需要在自己项目的静态资源目录中声明一个services文件,文件名为实现规范接口的类名全路径,此例中便是 com.spi.Printer,文件中的内同则为实现类的全路径,此例中便是com.spi.GoodPrinter.

这样一个厂商的实现便完成了。

4.bad-printer模块

和上述的good-printer的实现一样

4.1 bad-printer\pom.xml
<dependencies>
    <dependency>
        <groupId>com.lara</groupId>
        <artifactId>interface</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
4.2com.spi.BadPrinter
public class BadPrinter implements Printer {
    public void print() {
        System.out.println("you are bad");
    }
}
4.3在resource下添加services文件

5.invoker模块

这里的invoker便是我们自己的项目了。如果一开始我们想使用厂商bad-printer的Printer实现,是需要将其的依赖引入

<dependencies>
    <dependency>
        <groupId>com.lara</groupId>
        <artifactId>interface</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.lara</groupId>
        <artifactId>bad-printer</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
5.1编写调用主类
public class MainClass {
    public static void main(String[] args) {
        ServiceLoader<Printer> printLoaders = ServiceLoader.load(Printer.class);
        for (Printer printer : printLoaders){
            printer.print();
        }
    }
}

ServiceLoader是java.util提个的用于加载固定类路径下文件的一个加载器,正式它加载了对应接口声明的实现类。

5.2打印结果1

在后续的方案中,想替换厂商的Printer实现,只需要将依赖更换,调用主类无需变更代码,符合开闭原则。

5.3打印结果2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章