Java SPI机制

本文详细介绍了Java SPI(Service Provider Interface)机制的工作原理及其应用。通过具体示例展示了如何定义SPI接口、实现接口并利用ServiceLoader加载这些实现。

SPI全称Service Provider Interface,是Java提供的一种让第三方实现或扩展的API。

java平台中很多功能都是以这种方式提供接口给开发者调用的,最典型的如:JDBCJDNIJCE(Java加密扩展)JAXP等,看JDK源码或者第三方源码的时候会经常碰到SPI,所以我觉得很有必要写个笔记把SPI记录下来。

定义SPI接口

这里我就拿JDBC的数据库驱动作为例子,这里Driver类进行了简化。

早期的JDBC需要使用DriverManager手动注册数据库驱动类,JDBC4.0之后要求数据库驱动必须在 META-INF/services/java.sql.Driver文件中包含驱动类的全类名,以实现驱动类的自动注册(自动注册实际上就是用SPI实现)。

// 这个地方不要用java作为包名
package javaxx;

public interface Driver {

    void connect();

}

这里不要使用java作为包名,因为这个包名已经被JDK自己使用了,为了防止冲突Java平台不允许第三方使用这个作为包名,否则在后面的代码中会抛出SecurityException。

包名不允许为java

用以下命令编译这个接口,并打成jar包

:: 编译SPI接口
javac javaxx/Driver.java
:: 把SPI打成jar包,方便其他SPI的实现者添加依赖
jar cvf ../jar/driver-spi.jar javaxx/Driver.class

实现SPI接口

MySQL驱动简单实现

package mysql;

import javaxx.Driver;

public class MysqlDriver implements Driver{

    public void connect(){
        System.out.println("MySQL Driver Connected");
    }

}

为了能让Java平台发现这个SPI实现我们需要在META-INF/services目录下以SPI接口全类名作为文件名,并在该文件中写下实现类全类名。

在这个例子中就是创建META-INF/services/javaxx.Driver文件,并在文件中写下以下内容

# 文件中“#”号开头的内容会被解析成注释
# MySQL Provider Implement
mysql.MysqlDriver

我们把这个SPI实现打成jar包

:: 编译Mysql SPI实现
javac -classpath ../jar/driver-spi.jar mysql/MysqlDriver.java
:: 将编译的class文件打成jar包
jar -cvf ../jar/mysql-driver.jar mysql/ META-INF/

Oracle驱动简单实现

package oracle;

import javaxx.Driver;

public class OracleDriver implements Driver {

    public void connect(){
        System.out.println("Oracle Driver Connected");
    }

}

META-INF/services/javaxx.Driver文件中写下Oracle SPI的实现类:

# java.Driver Oracle Implement
oracle.OracleDriver

编译打包:

:: 编译Oracle实现
javac -classpath ../jar/driver-spi.jar oracle/OracleDriver.java
:: 打成jar包
jar -cvf ../jar/oracle-driver.jar oracle/ META-INF/

使用ServiceLoader加载SPI实现

java.util包下有个ServiceLoader工具类可以帮助我们加载类路径下所有的SPI接口的实现类

package cn.hff;

import javaxx.Driver;
import java.util.ServiceLoader;

public class Client {

    public static void main(String[] args) {
        ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
        // 遍历所有的Loader实现类
        for(Driver d : loader) {
            d.connect();
        }
    }

}

我们编译一下上面的Client类:

:: 编译
javac -classpath ../jar/driver-spi.jar cn/hff/Client.java

为了能让ServiceLoader找到MySQL和Oracle的SPI,运行时需要在将前面编译的jar包添加到类路径子下:

:: 运行
java -classpath .;../jar/driver-spi.jar;../jar/mysql-driver.jar;../jar/oracle-driver.jar; cn.hff.Client

示例代码地址:https://gitee.com/holmofy/Java-SPI-Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值