JDK SPI与Dubbo SPI的实现

JDK SPI

SPI全称Service Provider Interface,是提供厂商做插件开发的,如数据库驱动。

Java SPI使用了策略模式,一个接口多种实现。同时具体实现不在程序中指定,而是在程序之外的配置文件配置。

具体实现过程:

1. 新建一个名称为driver的maven工程,定义一个DatabaseDriver接口及对应的方法,打成jar包,供后续实现依赖

public interface DatabaseDriver {
    String buildConection(String msg);
}

2. 新建一个名称为mysql-driver的maven工程,在pom中引入driver工程的依赖,然后创建DatabaseDriver的实现类

public class MysqlDriver implements DatabaseDriver {
    @Override
    public String buildConection(String msg) {
        return "MySQL Driver:" + msg;
    }
}

3. 在mysql-driver工程main文件夹下创建资源文件夹resources,然后再创建目录META-INF/services,在该目录下创建以要实现接口全路径命名的文件:如com.xxx.spi.DatabaseDriver,文件内容为接口的实现的全路径名:com.xxx.spi.MysqlDriver

4. 新建一个名称为java-spi-test的工程,在pom中加入前面两个项目的依赖,可通过以下方式调用具体实现:

public static void main( String[] args ) {
    ServiceLoader<DatabaseDriver> serviceLoader = ServiceLoader.load(DatabaseDriver.class);

    for (DatabaseDriver databaseDriver : serviceLoader) {
        System.out.println(databaseDriver.buildConection("hello"));
    }
}

最后输出:MySQL Driver:hello

由此可以看出main方法中通过java.util.ServiceLoader可以获得接口的所有实现,具体使用哪种实现由用户决定。

Dubbo SPI

Dubbo 的扩展点机制是在 JDK SPI 机制上做了改进,引用官网的一段话:

Dubbo 改进了 JDK 标准的 SPI 的以下问题:

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
  • 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
  • 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

约定:

在扩展类的 jar 包内,在资源路径META-INF/dubbo(或META-INF/services、META-INF/dubbo/internal)下放置扩展点配置文件,文件名称为接口全限定名且无后缀,文件内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。

如果要定义dubbo SPI接口,需要在接口上添加@SPI注解,value为默认实现。

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> var1) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;

    void destroy();
}

以实现org.apache.dubbo.rpc.Protocol接口为例:

1. 首先定义实现类com.xxx.dubbo.spi.impl.MyProtocolImpl

public class MyProtocolImpl implements Protocol {

    @Override
    public int getDefaultPort() {
        return 8888;
    }

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        return null;
    }

    @Override
    public <T> Invoker<T> refer(Class<T> aClass, URL url) throws RpcException {
        return null;
    }

    @Override
    public void destroy() {
    }
}

2. 然后在resources/META-INF/dubbo(或META-INF/services、META-INF/dubbo/internal)目录下创建名称为org.apache.dubbo.rpc.Protocol的配置文件(接口的全限定名)

配置文件内容为:myprotocol=com.xxx.dubbo.spi.impl.MyProtocolImpl(key=value方式)

3. 最后可以通过以下方式调用dubbo的SPI实现类:

public static void main(String[] args) {
    Protocol myprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("myprotocol");
    // 输出实现类中的端口号8888
    System.out.println(myprotocol.getDefaultPort());
}

发布了149 篇原创文章 · 获赞 100 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/u011212394/article/details/102724963