Master Java SPI: Build Decoupled Services with Service Provider Interface
This guide explains Java's Service Provider Interface (SPI), detailing its purpose for decoupling implementations, step-by-step project setup, code examples, configuration files, and an in-depth analysis of ServiceLoader's internal mechanisms, enabling developers to create flexible, extensible Java applications.
1. Introduction to SPI
SPI (Service Provider Interface) is a mechanism in Java that allows discovery of implementations, enabling decoupling between interfaces and their implementations. For example, JDBC uses SPI to load database drivers, and frameworks like Spring Boot and Dubbo also leverage it.
2. SPI Getting Started Example
2.1 Create Projects
Four Maven projects are created:
spi-interface – defines the Person interface.
spi-impl1 – provides the Teacher implementation.
spi-impl2 – provides the Student implementation.
spi-test – loads all implementations via SPI.
2.2 Define SPI Interface
package com.jd.spi;
public interface Person {
String favorite();
}2.3 Create Implementation 1 (Teacher)
2.3.1 Class
package com.jd.spi;
public class Teacher implements Person {
public String favorite() {
return "老师喜欢给学生上课";
}
}2.3.2 SPI configuration file
In resources/META-INF/services/com.jd.spi.Person add the fully qualified name of Teacher.
2.4 Create Implementation 2 (Student)
2.4.1 Class
package com.jd.spi;
public class Student implements Person {
public String favorite() {
return "学生喜欢努力学习";
}
}2.4.2 SPI configuration file
Similarly, add Student 's fully qualified name to resources/META-INF/services/com.jd.spi.Person.
2.5 Create Test Project
2.5.1 Maven dependencies
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>spi-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>spi-impl1</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>spi-impl2</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>2.5.2 Test class
package com.jd.spi;
import java.util.Iterator;
import java.util.ServiceLoader;
public class SPITest {
public static void main(String[] args) {
ServiceLoader<Person> loader = ServiceLoader.load(Person.class);
for (Iterator<Person> it = loader.iterator(); it.hasNext(); ) {
Person person = it.next();
System.out.println(person.favorite());
}
}
}Running the test prints:
Java's SPI mechanism discovers all Person implementations and invokes their favorite() method.
3. SPI Mechanism Principles
3.1 Core fields of ServiceLoader
public final class ServiceLoader<S> implements Iterable<S> {
private static final String PREFIX = "META-INF/services/";
private final Class<S> service;
private final ClassLoader loader;
private final AccessControlContext acc;
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
private LazyIterator lookupIterator;
}These fields are used to locate configuration files under META-INF/services.
3.2 ServiceLoader iterator
public Iterator<S> iterator() {
return new Iterator<S>() {
Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext()) return true;
return lookupIterator.hasNext();
}
public S next() {
if (knownProviders.hasNext()) return knownProviders.next().getValue();
return lookupIterator.next();
}
public void remove() { throw new UnsupportedOperationException(); }
};
}The iterator ultimately calls lookupIterator.next().
3.3 lookupIterator.next() and nextService()
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
private S nextService() {
if (!hasNextService()) throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try { c = Class.forName(cn, false, loader); }
catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); }
if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); }
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); }
throw new Error(); // cannot happen
}3.4 hasNextService()
private boolean hasNextService() {
if (nextName != null) return true;
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
configs = (loader == null) ? ClassLoader.getSystemResources(fullName) : loader.getResources(fullName);
} catch (IOException x) { fail(service, "Error locating configuration files", x); }
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) return false;
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}The process can be summarized as:
Create a ServiceLoader instance.
Create the lazy lookupIterator.
Use hasNextService to read all META-INF/services files for the interface and store their contents.
Load each implementation class via reflection and instantiate it.
Key requirements: the configuration file must reside under META-INF/services with the interface's fully qualified name, and each implementation must provide a no‑argument constructor.
4. Conclusion
This article introduced Java's SPI mechanism, demonstrated basic usage with a complete example, and examined its internal implementation. Mastering SPI enables developers to design flexible, decoupled systems and deepens their technical expertise.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
