This one is about the Java Services API or Java Service Provider Standard, which i totally agree to be a very very long name, so lets just name it Java SPI API.
SPI stands for Service-Provider Interface. The key concept here is that there is a service contract (an interface) and there might be one or more implementations of such a service contract in your classpath.
The Java SPI API allows developers to easily locate and load such implementations. It’s the plug-and-play API that Java offers since Java 6. That’s right, I said 6 and we are now in Java 9 and you have probably not been exploring this.
It scans each jar in your classpath in a pretty efficient way, looking for implementations of the specified service interface.
This feature was initially meant to only be used by the JDK internally and the JDBC API is one of those APIs that is now taking advantage of the JAVA SPI. But how? what for?
The JDBC example
I could have picked a different API but I picked JDBC because I wouldn’t have to create much context.
Context
JDBC is basically a database connectivity framework that allows Java applications to connect to different database management systems and perform operations. The Key concept of the JDBC API is the JDBC Driver.
Each database management system must provide a JDBC Driver implementation. The JDBC specification defines the JDBC driver contract in java.sql.Driver.
package java.sql
public interface Driver {
Connection connect(String url, java.util.Properties info)
throws SQLException;
//More methods come here
}
To connect to a mysql database for example, we must add the Mysql J connector to our classpath. This connector provides an implementation of the JDBC Driver contract.
Adding the connector to the classpath is NOT enough to connect to the database. We must make sure the Mysql driver is registered to the DriverManager. How do we do that? Loading the driver class will automatically register the Mysql driver, because it will trigger the execution of all the static blocks of the class. I said static blocks, not static methods. The mysql driver class has a static block that performs the driver registration.
In case you don’t know whats a static block, i will give you a real quick example:
public class ExampleClazz {
static {
String msg = "This will be executed once this class is loaded";
System.out.println(msg);
}
}
Now that I’m sure you understand what’s a static block, lets carry on. As I was saying, we need to load the class in order to trigger the execution of all the static blocks. And the Mysql driver implementation class com.mysql.jdbc.Driver has a static block that performs such registration.
Loading the mysql driver class should be enough to register it:
public class Example {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception ex) {
// handle the error
}
}
}
From this point on, you can connect to a mysql database, it will just work.
I know you didn’t signup for this boring Mysql tutorial. Sorry for that. Just wanted to make sure you understand the Driver registration process. Now, lets find an answer for that question: how is JDBC leveraging the SPI API?
JDBC and Java SPI
You do remember what we just did up there right? Thats a rhetorical question by the way. I know you do. We had to manually register the Mysql JDBC driver. From Java 6 on we don’t have to register the JDBC driver manually before we use it, as long as the Jar containing the Driver implementation is Java SPI complaint, the DriverManager will automatically find and instantiate it using Java SPI.
The Java Service Provider Standard
If the Jar file containing the implementation class(es) is not complaint to the Java SPI, the implementation classes wont be found via the SPI API.
Creating a new JDBC driver JAR complaint to Java SPI
Let’s say we have an example-drivers.jar file with the following structure:
+example-drivers.jar | +com | | +mario | | | +driver | | | DriverA.class DriverB.class | | |
The example-drivers.jar file contains two Driver implementations. It could contain one or more, doesn’t matter, as long as it contains any. In order for it to be compliant, we must explicitly declare all the Driver implementations contained in that jar file. Notice the new file that we will add to the jar:
+example-drivers.jar | +com | | +mario | | | +driver | | | DriverA.class DriverB.class | +META-INF | | | +services | | | java.sql.Driver
We just added a new file to /META-INF/services/java.sql.Driver. The file content is the following:
com.mario.driver.DriverA com.mario.driver.DriverB
The filename is the name of the service interface, which in this case is java.sql.Driver. The file content is the list of implementation classes. One implementation class per line. Notice that the file has NO extension. That file we just added at that specific path, is what will allow the DriverManager to locate and load that two Driver implementations via the Java SPI API.
But how do we use the Java SPI API? We already know how a complaint jar file should look like, but how do we ask the SPI API to locate implementations of a specific interface?
Loading all available Driver implementations
This is the funnier step. A few code lines and you are good to go:
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
Iterator<Driver> drivers = loader.iterator();
while(drivers.hasNext()){
Driver driver = drivers.next();
//You can now do whatever you want with the driver object
}
Honestly, I do not expect you to be developing JDBC drivers. That’s not the point. I expect you to adopt the Java SPI API to develop extensible Java applications.
Besides the JDBC, there other java core APIs that are using this API to enable extensibility. For example, the Java Security API uses it to locate digest and encryption algorithms and authentication modules implementations. The Java Sound API and the Java Image API also use this API and a ton of other APIs that you don’t have to worry about also use this API. It’s not about any of these APIs. It’s about exploring the SPI API itself and taking advantage of it to add functionality to your applications without having to recompile/package your entire app.
Don’t for a second think that this API only matters for Java SE and that its irrelevant for Java-EE. You can leverage this regardless the environment your java code will run. Also, keep in mind that any implementation must be in the classpath in order to be found by the SPI ServiceLoader API, unless you provide a classloader instance, anyways, the point is: in order to have a complete plug-and-play scenario your application must be able to detect a new jar in your classpath and consider it during the scanning process. This is a process that deserves a special attention by itself because it requires you to understand the classloader concept, which for the sake of comprehension, will be explained on its own article.
What do you think? Would you use this API? Share your thoughts in the comments.
It’s always a pleasure!







Comentários Recentes