I am creating a framework that exposes an API for developers to use:
public interface MyAPI {
public void doSomeStuff();
public int getWidgets(boolean hasRun);
}
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
).
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)?
Bonus points for concrete, working Java code example! Thanks in advance!
Service provider interface (SPI) is an API intended to be implemented or extended by a third party. It can be used to enable framework extension and replaceable components.
API stands for Application Programming Interface, where API is a means for accessing a service / function provided by some kind of software or a platform. SPI stands for Service Provider Interface, where SPI is way to inject, extend or alter the behavior for software or a platform.
SPI stands for Service Provider Interface. It is a subset of all things that can be API specific to situations where a library is providing classes which are called by the application (or API library), and which typically change the things the application is able to do. The classic example is JavaMail.
In Java, a service is defined by a set of interfaces and classes. The service contains an interface or an abstract class that defines the functionality provided by the service. There are multiple implementations for a service and they are called as service providers.
Take a look at the java.util.ServiceLoader class.
In general, the idea is this:
API Jar
Binding/Driver Jar
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
EDIT
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;
}
}
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.
If you want to hide the accepts method, you could always have a second interface which is your public API, and t
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With