SPI(Service Provider Interface) is an API. It can be customizable by a third-party provider. We can change the existing implementation or extendable. In this, a Service is an extension of the set of interfaces containing different methods, and Service Provider is an implementation of the service. General features are given in the service via Classes. A Service Provider when it is created will provide a feature to customize the provider according to the requirements. The main advantage of the service provider is it will not change the original code base but it will provide the additional functionalities to extend the code base
Installation of Service Providers is done in two ways:
- As plug-ins in the form of a JAR file and can copy to the classpath on the application. This is called local installation.
- As a plug-in in the form of a JAR file and copy the JAR file JRE/lib/ext folder in the JAVA_HOME path. This way of installation is a global installation.
Loading Service Providers
This is a very important step. JDK needs to be known about the availability of service providers. A new file under META-INF/services folder in the application classpath has to be created for this. META-INF/services folder filename should be the fully qualified binary name of the service, in that we need to specify all the valid list of fully qualified binary names of service provider classes.
Service Loader
This is the main part of the Service Provider Interface. It has the responsibility of discovering the loading of the SPI implementations lazily. It is used to scan the application classpath to discover the provider implementations and maintained in a cache.
Examples of SPI
java.util.spi.ConcurrencyNameProvider | Provides the localized currency symbols for Currency |
java.util.spi.LocaleNameProvider | Provides the localized names for Locale. |
java.util.spi.TimeZoneNameProvider | Provides the localized timeZone for Timezone. |
java.text.spi.DateFormatProvider | Provides date and time formats for a specified locale |
java.text.spi.NumberFormatProvider | Provides monetary, integer, and percentage values for the NumberFormat class |
Let us see a small example of building a customized Service Provider
Example Project
Project Structure:
It is a maven-based project. Let’s start with pom.xml
pom.xml
XML
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < groupId >org.gfg</ groupId > < artifactId >Sample-Account-api</ artifactId > < version >0.0.1-SNAPSHOT</ version > < packaging >jar</ packaging > < name >Sample-Account-api</ name > < properties > < project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > </ properties > < build > < plugins > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-compiler-plugin</ artifactId > < version >3.8.0</ version > < configuration > < source >1.8</ source > < target >1.8</ target > </ configuration > </ plugin > </ plugins > </ build > </ project > |
Let us start by seeing the service interface first.
AccountInterface.java
Java
public interface AccountInterface { public String getAccountType(String accountType); public int getAccountId( int accountId); } |
We can add methods matching business requirements. As of now, 2 methods are given for simplicity. Implement the methods now via
AccountServiceProvider.java
Java
import org.gfg.spi.AccountInterface; public class AccountServiceProvider implements AccountInterface{ @Override public String getAccountType(String accountType) { // can do any business logic here return " Your account type is " + accountType; } @Override public int getAccountId( int accountId) { return accountId; } } |
Let’s create the main method via the App.java file
App.java
Java
import java.util.Iterator; import java.util.ServiceLoader; import org.gfg.spi.AccountInterface; public class App { public static void main( String[] args ) { ServiceLoader<AccountInterface> loader =ServiceLoader.load(AccountInterface. class ); Iterator<AccountInterface> iter = loader.iterator(); while (iter.hasNext()){ AccountInterface account = iter.next(); System.out.println(account.getAccountType( "Savings" )); System.out.println(account.getAccountId( 1000 )); } } } |
Very important and essential step:
Under the resources folder, we need to create META-INF/services folder. A file named “org.gfg.spi.AccountInterface” has to be created and it should have content as
org.gfg.spi.providers.AccountServiceProvider
We have to give it correctly. Otherwise, the program will not execute correctly. Now run App.java. We can see the below output
Conclusion
- Service provider framework is an easy way to decouple.
- It can load multiple service implementations of the given Service Interface.
- The very essential and important step is that all Service Provider classes must be in the META-INF/services folder with the fully qualified name of the Service Interface.