How to implement API / SPI pattern in Java?

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

public interface MyAPI {
    public void doSomeStuff();

    public int getWidgets(boolean hasRun);
}

What all developers should do is write their projects according to these API methods I also hope they can place different "drivers" / "API bindings" (the same way as JDBC or slf4j) on the runtime classpath, and have API method calls (dosomestuff()) on different third-party resources (files, servers, any) Therefore, according to the driver / binding (i.e. myapi FTP, myapi SSH, myapi reporting) seen in the runtime classpath, the same code and API calls will be mapped to operations on different resources

How do I write (and package) an SPI that allows such runtime binding, and then map my API calls to the correct (concrete) implementation? In other words, if myapi FTP allows you to get widgets (Boolean) from the FTP server, what should I do (using API and SPI)?

Bonus points, specific work, java code example! Thank you in advance!

Solution

Look at Java util. Serviceloader class

Generally speaking, the idea is:

API tank

>Provide interface > use serviceloader class to find implementation

Binding / driver tank

>Implementation interface > create file meta inf / and specify the class name to implement it

There is a good example in Javadoc:

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

API tank

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.
  }

Binding can

package com.myfoo;
public class MyFooImpl implements FooInterface { ... }

Meta-INF/com.foo.FooInterface
    com.myfoo.MyFooImpl

edit

SPI example

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;
  }
}

Of course, change the file name to com foo. Foospi and provide the implementation of foospi This will allow you to isolate the public API from the SPI interface

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

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>