一、SPI概念
这里先说下SPI的一个概念,SPI英文为Service Provider Interface单从字面可以理解为Service提供者接口,正如从SPI的名字去理解SPI就是Service提供者接口;我对SPI的定义:提供给服务提供厂商与扩展框架功能的开发者使用的接口。
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
很多框架都使用了java的SPI机制,如java.sql.Driver的SPI实现(mysql驱动、oracle驱动等)、common-logging的日志接口实现、dubbo的扩展实现等等框架;
基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。
jdk提供服务实现查找的一个工具类:java.util.ServiceLoader.
二、SPI机制的说明:
1) 在META-INF/services/目录中创建以接口全限定名命名的文件该文件内容为Api具体实现类的全限定名
2) 使用ServiceLoader类动态加载META-INF中的实现类
3) 如SPI的实现类为Jar则需要放在主程序classPath中
4) Api具体实现类必须有一个不带参数的构造方法
三、SPI的一个小demo
假设有一个内容搜索系统,分为展示和搜索两个模块。展示和搜索基于接口编程。搜索的实现可能是基于文件系统的搜索,也可能是基于数据库的搜索。实例代码如下
Search.java: 搜索接口
package com.licq.spi;
import java.util.List;
public interface Search {
List<Object> search(String keyword);
}
下面定义两实现类
FileSearch.java:文件系统的搜索实现
package com.licq.spi;
import java.util.List;
public class FileSearch implements Search {
public List<Object> search(String keyword) {
// TODO Auto-generated method stub
System.out.println(" FileSearch search.....keywords:"+keyword);
return null;
}
}
DatabaseSearch.java实现类
package com.licq.spi;
import java.util.List;
public class DatabaseSearch implements Search {
public List<Object> search(String keyword) {
// TODO Auto-generated method stub
System.out.println(" databaseSearch search.....keywords:"+keyword);
return null;
}
}
Client.java测试类:
package com.licq.spi;
import java.util.Iterator;
import java.util.ServiceLoader;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
ServiceLoader<Search> s = ServiceLoader.load(Search.class);
Iterator<Search> searchs = s.iterator();
while(searchs.hasNext()){
Search sea = searchs.next();
sea.search("hello world.");
}
}
}
import java.util.Iterator;
import java.util.ServiceLoader;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
ServiceLoader<Search> s = ServiceLoader.load(Search.class);
Iterator<Search> searchs = s.iterator();
while(searchs.hasNext()){
Search sea = searchs.next();
sea.search("hello world.");
}
}
}
最后创建在META-INF/searvices/com.licq.spi.Search文件。
当 com.licq.spi.Search文件内容是”com.licq.spi.FileSearch”时,程序输出是:
FileSearch search.....keywords:hello world.
当com.licq.spi.Search文件内容是”com.licq.spi.DatabaseSearch”时,程序输出是:
databaseSearch search.....keywords:hello world.
可以看出Client.java里没有任何和具体实现有关的代码,而是基于spi的机制去查找服务的实现。很多开源框架也是基于这种机制去实现的,具体的完整源码demo可以在Github上去查看。