Java架构-Java 规范 SPI

1,928 阅读3分钟

我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。

为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

1、什么是SPI

这里先说下SPI的一个概念,SPI英文为Service Provider Interface单从字面可以理解为Service提供者接口,正如从SPI的名字去理解SPI就是Service提供者接口;我对SPI的定义:提供给服务提供厂商与扩展框架功能的开发者使用的接口。

很多框架都使用了java的SPI机制,如JDBC4中的java.sql.Driver的SPI实现(mysql驱动、oracle驱动等)、common-logging的日志接口实现、dubbo的扩展实现等等框架;

2、如何编写SPI

当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。 JDK提供服务实现查找的一个工具类:java.util.ServiceLoader。

3、Example

1)Interface

public interface Speak {

    void sayHello();

}

2)Implement

有2个不同的实现,修改配置文件的时候就会实例化不同的对象.

public class SpeakImpl implements Speak {
    @Override
    public void sayHello() {
        System.out.println("haha");
    }
}

另外一个实现:

public class AnnSpeak implements Speak {
    @Override
    public void speak() {
        System.out.println("hi, ann!");
    }
}

3)Config 在resources/META-INF.services文件下添加以接口全路径命令的文件:

  • resources

  • META-INF.services

  • com.weimob.jdk.spi.api.Speak

    然后在文件中添加这个接口的实现。

     com.weimob.jdk.spi.impl.CarlSpeak
    

4)Test

public class SPITest {

    public static void main(String[] args) {

        ServiceLoader<Speak> s = ServiceLoader.load(Speak.class);
        Iterator<Speak> searchs = s.iterator();
        if(searchs.hasNext()){
            Speak speak = searchs.next();
            speak.speak();
        }
    }

}

5)运行结果

修改配置文件为:

com.weimob.jdk.spi.impl.AnnSpeak

运行结果就是:

可以看出SPITest里没有任何和具体实现有关的代码,而是基于spi的机制去查找服务的实现。因为这只是举例这个例子是在同一个项目中运行的。在真实的环境中你可以接口与实现分离这也是能够运行成功的。

4、Q&A

1、配置文件为什么要放在META-INF/services下面? 我们可以在java.util.ServiceLoader中找到以下代码。

  private static final String PREFIX = "META-INF/services/";

2、ServiceLoader读取实现类是什么时候实例化的?

ServiceLoader.LazyIterator.nextService中实例化,即load的结果迭代时才会被实例化。

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜! 

合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!
To-陌霖Java架构

分享互联网最新文章 关注互联网最新发展