Java中的SPI

SPI是service provider interface的简写。本篇文章就讲解下SPI是做什么用的,以及怎么来使用。SPI这个东西还是非常好用的。

SPI是什么

SPI主要解决的还是耦合问题。我们知道面向接口编程是很好的编程方式,Java中会提前定义一些规范,一些接口,而接口的实现可以由第三方实现,只要其遵循接口实现规范即可。比如大家比较熟悉的JDBC就是这样的。

提到SPI一般不得不提下类加载器ClassLoader。我们知道JVM中由三个类加载器,BootstrapClassLoader, ExtClassLoader,AppClassLoader。其采用双亲委派模式。同时JDK也建议我们在加载类的时候使用这种方式。但是如果SPI使用这种方式就会导致找不到类的问题。

以JDBC为类简单说明一下,使用双亲委派模式的问题:

  • 首先,我们知道JDBC中的接口是Java的核心包,在rt.jar中,这个jar是由BootstrapClassLoadre来加载的。
  • 其次,SPI的加载规则是根据jar包中META-INF下services下的文件来查找对应实现类的。在META-INF下services下会定义一个文件,其文件名是接口类的全类型,而文件的内容是实现类的全类名。
  • 再次,实现类的加载是在DriverManager中来调用,通过Class.forName来加载实现类,而Class.forName使用的是当前类的类加载器,当前类的类加载器是BootstrapClassLoader,我们知道BootstrapClassLoader默认是加载rt.jar的。明显第三方实现不在rt.jar
  • 最后,怎么解决这个问题呢,SPI是采用ContextClassLoader来加载第三方实现类,这样就避免了父ClassLoader-BootstrapClassLoader去应该由加载子ClassLoader-AppClassLoader加载的类。而ContextClassLoader就是被设计来解决这样的问题的。

SPI怎么使用。

  • 首先实现个接口

  • 其次弄个接口实现类

  • SPI需要在jar包的META-INF下的services下建一个文件,SPI就是根据这个文件来找到实现类的。

文件名:接口的全类名
文件内容: 实现类的全类名
复制代码
  • 最后,通过ServiceLoader来加载,面向接口编程,达到解耦合的目的。
Hello hello = null;
ServiceLoader<Hello> serviceLoader = ServiceLoader.load(Hello.class);
Iterator<Hello> iter = serviceLoader.iterator();
if (iter.hasNext()) {
    hello = iter.next();
}
System.out.println(hello.say());
复制代码

总结

通过上面的叙述,可以看到SPI应该有很多的用处的,定义接口规范,而接口的实现可以由不同的第三方来实现,同时在编程的过程中,还可以面向接口编程,代码写起来那是非常的优雅。

猜你喜欢

转载自juejin.im/post/5df1d3eff265da33ed411c5d