Spi的背景:
1)我们现在的编程主要是接口编程,模块之间不对实现进行硬编码,
2)一旦代码中涉及具体的实现,就违反了可插拔的原则,如果要替换一种实现就需要修改代码。
spi的用处:
为了实现模块装配的时候不写死代码,spi提供了一种服务发现机制,有点类似IOC的思想,将装配的控制权转移到代码外,意思就是你如果在装配的时候有2个实现类,那么你可以通过配置文件来写要加载的实现类。
Spi的缺点:
1)Jdk标准的SPI会一次性实例化扩展点所有的实现,由于是全部加载不管是否2)有使用到所有很浪费资源
没有IOC和AOP的支持
Spi的约定
1)一个提供者,提供了一个接口并存在多种实现的时候。
2)一般会在Jar包的META-INF/services/目录下,创建一个接口的路径+接口名称的文件,该文件的内容就是该接口具体的实现类的名称,当外部程序装配该模块的时候就可以通过该jar包META-INF/services/里面的配置文件找到对应的实现类名,并完成模块的注入、装载实例化。(jdk提供了一个工具类来实现查找服务:java.util.ServiceLoader)。
spi的简单例子
接口定义
package spi;
public interface SayHello {
public void hello();
}
2个实现类
package spi;
public class MingHello implements SayHello{
@Override
public void hello() {
System.out.println("您好!我是明");
}
}
package spi;
public class YueHello implements SayHello {
@Override
public void hello() {
System.out.println("您好!我是月");
}
}
META-INF.services下的实现类加载文件
文件名:spi.SayHello
文件内容
spi.MingHello
spi.YueHello
程序入口
package spi;
import java.util.ServiceLoader;
public class MainSpi {
public static void main(String[] args) {
ServiceLoader<SayHello> sayHesLoader = ServiceLoader.load(SayHello.class);
for(SayHello sayHes:sayHesLoader){
sayHes.hello();
}
}
}