Design Pattern - Service Provider Interface (SPI)
Design Pattern - Service Provider Interface (SPI)
Design Pattern - Service Provider Interface (SPI)
SPI is an API intended to be implemented or extended by third party. It can be used to enable
framework extension and replaceable component.
A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific
implementation of a service. The classes in a provider typically implement the interfaces and subclass
the classes defined in the service itself. Service providers can be installed in an implementation of the
Java platform in the form of extensions, that is, jar files placed into any of the usual extension
directories. Providers can also be made available by adding them to the application's class path or by
some other platform-specific means.
Need of SPI
One interface, many implementations is a very well-known object oriented programming paradigm. If
you write the implementations yourself then you know what those implementations are and you can
write a factory class or method that creates and returns the right implementation. You might also make
this configuration driven and inject the correct implementation based on configuration (spring injection
based on name)
What if third parties are providing implementations of your interface? If you know those
implementations in advance, then you could do the same as in the case above. But one downside is that
code change is required to add or use new implementations or to remove them. You could come up
with a configuration file, where implementations are listed and your code uses the list to determine
what is available. Downside is that configuration has to be updated by you and this is nonstandard
approach.
Java has a solution for that. Java 1.6 introduced ServiceLoader<T> class for discovering and loading
implementation classes dynamically at runtime. It has one method:
File which specifies implementation classes name can contain multiple entries. There could be a chance
that multiple developer provided implementation and multiple jars are available in class path. So java,
scan all those jars, get list of implementation classes.
Later you can decide what you want to do on those classes. Either you can store those classes in map by
some name, based on user input, certain implementation class can be executed or can be executed
directory as extension of something.
JDBC 4.0 supports automatic loading of JDBC driver class instead of using Class.forName(). It’s done
using SPI. Every JDBC 4 compliant driver has a file in its jar called META-INF/services/java.sql.Driver, in
that file it will list its implementation(s) of java.sql.Driver. When you request a connection,
DriverManager will use the ServiceLoader (static block) to find all copies of META-
INF/services/java.sql.Driver in the classpath and will then load all classes listed. Each driver
implementation registers itself with DriverManager.
When you request a connection from DriverManager, the DriverManager will iterate over all registered
drivers asking them for a Connection. The driver will use the JDBC url to check if it's a protocol it
supports.
If the driver does not support the protocol, it will return null, if it does support the protocol it will either
return an established connection, or it will throw an SQLException (eg if you made an error in the URL,
or it couldn't connect). If all drivers return null (none support the protocol), then DriverManager will
throw an SQLException with error "No suitable driver found for <url>"
If there are multiple drivers for the same database (or at least: same protocol prefixes), it will use the
first in the list of drivers. Depending on the Java version, if that driver fails with an SQLException, it will
continue with the next driver (at least Java 5 and later)