1、什么是 API 和 SPI
API(Application Programming Interface)
大多数情况下,都是实现方来制定接口并完成对接口的不同实现,调用方仅仅依赖并直接进行使用却无权选择不同实现。
SPI(Service Provider Interface)
SPI 是JDK 提供的一种服务提供发现机制, 调用方来制定接口,实现方来针对接口来进行不同的实现。调用方来选择自己需要的实现方。
2、API 和 SPI 用途
API 用途:
API 可以理解为服务方暴露自己的服务供客户调用,JDK 就作为服务方,提供了一系列的 api 可以直接使用的方法供客户方开发人员直接进行使用,从而达到某种功能的实现
SPI 用途
SPI 是服务方不再提供具体的方法,而是提供对象的接口,客户方需要实现接口,然后服务方调用客户方的接口实现类,再来实现客户的某种功能。
在 JDBC 连接数据库时,针对不同的数据库,可能有不同的数据库驱动实现。不同数据库的 Driver 驱动通过 DriverManager 来进行管理,我们在使用 Class.forName("driverName")
加载驱动时,就会执行其中的静态代码块,将 Driver 注册到 DriverManager 中供后面进行使用。
Java 中定义了 java.sql.Driver
接口,并没有具体的实现,具体实现由各厂商来提供,通过厂商提供的驱动包来连接自己的数据库。
3、SPI 应用实例
当服务的提供者提供了一种借口的实现后,需要在 classpath 下的 META-INF/services
目录里创建一个以服务接口命名的文件,这个文件内容就是接口的具体实现类。
其它程序需要调用这个服务的时候,可以查找这个下面的配置文件,根据这个文件中的类名加载实例化,就可以使用该服务了
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url,username,password);
在上述代码中没有加载驱动,我们怎么确定使用的是哪个数据库连接的驱动呢?
在 DriverManager 中有一个静态方法
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
这个 loadInitialDrivers
方法使用了SPI 工具类 ServiceLoader
在这个方法中会搜索 classpath 下所有 META-INF/service
目录下的 java.sql.Driver 文件,并找到文件中实现类的名字
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});