如何在 Java 中实现 API/SPI 模式? [英] How to implement the API/SPI Pattern in Java?

查看:19
本文介绍了如何在 Java 中实现 API/SPI 模式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个公开 API 供开发人员使用的框架:

I am creating a framework that exposes an API for developers to use:

public interface MyAPI {
    public void doSomeStuff();

    public int getWidgets(boolean hasRun);
}

开发人员应该做的就是根据这些 API 方法编写他们的项目.我还希望他们能够在运行时类路径上放置不同的驱动程序"/API 绑定"(与 JDBC 或 SLF4J 的工作方式相同)并具有 API 方法调用(doSomeStuff() 等).) 在不同的 3rd 方资源(文件、服务器等)上运行.因此,相同的代码和 API 调用将映射到不同资源上的操作,具体取决于运行时类路径看到的驱动程序/绑定(即 myapi-ftpmyapi-sshmyapi-teleportation).

All the developers should have to do is code their projects against these API methods. I also want them to be able to place different "drivers"/"API bindings" on the runtime classpath (the same way JDBC or SLF4J work) and have the API method calls (doSomeStuff(), etc.) operate on different 3rd party resources (files, servers, whatever). Thus the same code and API calls will map to operations on different resources depending on what driver/binding the runtime classpath sees (i.e. myapi-ftp, myapi-ssh, myapi-teleportation).

我如何编写(和打包)允许此类运行时绑定的 SPI,然后将 MyAPI 调用映射到正确的(具体)实现?换句话说,如果 myapi-ftp 允许您从 FTP 服务器 getWidgets(boolean) ,我怎么能做到这一点(同时使用 API 和 SPI)?

How do I write (and package) an SPI that allows for such runtime binding, and then maps MyAPI calls to the correct (concrete) implementation? In other words, if myapi-ftp allows you to getWidgets(boolean) from an FTP server, how would I could this up (to make use of both the API and SPI)?

具体的、有效的 Java 代码示例的加分项!提前致谢!

Bonus points for concrete, working Java code example! Thanks in advance!

推荐答案

看看 java.util.ServiceLoader 类.

Take a look at the java.util.ServiceLoader class.

总的来说,这个想法是这样的:

In general, the idea is this:

API 罐

  • 提供接口
  • 使用 ServiceLoader 类查找实现

  • Supply the interface
  • Use the ServiceLoader class to lookup the implementation

    绑定/驱动程序 Jar

    Binding/Driver Jar

  • 实现接口
  • 创建文件 META-INF/并指定实现它的类名

  • Implement the interface
  • Create the file META-INF/ and specify the classname that implements it

    javadoc 中有一个很好的例子:

    There's a good example right in the javadocs:

    http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html

    package com.foo;
    
    public interface FooInterface { ... }
    
    public class FooInterfaceFactory {
      public static FooInterface newFooInstance() {
        ServiceLoader<FooInterface> loader = ServiceLoader.load(FooInterface.class);
        Iterator<FooInterface> it = loader.iterator();
        // pick one of the provided implementations and return it.
      }
    

    绑定罐

    package com.myfoo;
    public class MyFooImpl implements FooInterface { ... }
    
    META-INF/com.foo.FooInterface
        com.myfoo.MyFooImpl
    

    编辑

    public interface FooSpi { 
       void accepts(String url);
       FooInterface getFooInstance();
    }
    
    
    public class FooInterfaceFactory {
      public static FooInterface getFooInterfaceInstance(String url) {
        ServiceLoader<FooSpi> loader = ServiceLoader.load(FooSpi.class);
        Iterator<FooSpi> it = loader.iterator();
        while (it.hasNext()) {
           FooSpi fooSpi = it.next();
           if (fooSpi .accepts(url)) {
             return fooSpi.getFooInstance();
           }
        }
    
        return null;
      }
    }
    

    当然,将文件名更改为 com.foo.FooSpi 并提供 FooSpi 的实现.这将允许您将公共 API 与 Spi 接口分开.

    And of course, change the filename to com.foo.FooSpi and provide an implementation of FooSpi. That will allow you to segregate the public API from the Spi interface.

    如果你想隐藏 accepts 方法,你总是可以有第二个接口,它是你的公共 API,并且 t

    If you want to hide the accepts method, you could always have a second interface which is your public API, and t

    这篇关于如何在 Java 中实现 API/SPI 模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆