1.1什么叫SPI

SPI的全名为Service Provider Interface,单从字面可以理解为Service提供者接口,正如从SPI的名字去理解SPI就是提供给服务提供厂商与扩展框架功能的开发者使用的接口。
简单的总结下javaSPI机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

1.2 SPI的用法

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

1.3代码实现

1.1.1创建工程

首先创建如下几个工程:

java线上热插拔 java模块化热插拔_spring boot

1.1.2创建接口

在service-common工程中创建一个接口:

package cn.tx.service;

public interface PayService  {
    public void pay();
}

1.1.3为工程引入接口

在weixinPay和zhifubaoPay以及test中添加service-common依赖:

<dependencies>
        <dependency>
            <artifactId>service-common</artifactId>
            <groupId>cn.tx.spingboot</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

这样就可以在类中引入接口了。

1.1.4创建实现类

在weixinPay和zhifubaoPay中分别创建实现类实现PayService()接口:

package cn.tx.service.impl;

import cn.tx.service.PayService;

public class WeixinPay implements PayService {

    @Override
    public void pay() {
        System.out.println("微信已付款");
    }
}
package cn.tx.service.impl;

import cn.tx.service.PayService;

public class ZhifubaoPay implements PayService {

    @Override
    public void pay() {
        System.out.println("支付宝已付款");
    }
}

1.1.5创建实现类

在test中除了要引入接口外,也要引入实现类:

<dependency>
			<!--或者weixinPay-->
            <artifactId>zhifubaoPay</artifactId>
            <groupId>cn.tx.spingboot</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

接下来,在测试类中实现有关方法:

package cn.tx;

import cn.tx.service.PayService;

import java.util.ServiceLoader;

public class test {
    public  static void main(String[] args){
    	//这里用到实现查找的一个工具类:java.util.ServiceLoader。
        ServiceLoader<PayService> load = ServiceLoader.load(PayService.class);	
        for (PayService payService:load){
            System.out.println(payService);	
        }
    }
}

测试:

java线上热插拔 java模块化热插拔_jar包_02


结果发现没有打印出来,原因:不符合spi规范。

我上面提到,当外部程序装配这个模块的时候,通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。由于我们没有创建该目录的文件,所以它找不到相关的实现类。

解决办法:在工程中的resources下创建如下文件:

java线上热插拔 java模块化热插拔_spring boot_03


然后将之前所写的接口的全路径复制,在文件下新建一个文件:

java线上热插拔 java模块化热插拔_java线上热插拔_04


再将实现类的全路径复制到文件中:

java线上热插拔 java模块化热插拔_spring boot_05


这样再去测试就可以读取到了。