The SPI (Service Provider Interface) is a built-in service provider discovery mechanism in JDK.It can find service implementations for a specific interface.Demo:
public class TestSpi {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
ServiceLoader<SpiInterface> load = ServiceLoader.load(SpiInterface.class);
Iterator<SpiInterface> iterator = load.iterator();
if (iterator.hasNext()) {
SpiInterface next = iterator.next();
next.spiInterFaceMethod("hello");
}
}
}
Interface:
public interface SpiInterface {
public List<String> spiInterFaceMethod(String keyword);
}
SPI Implementation Class:
public class FileSearch implements SpiInterface {
@Override
public List<String> spiInterFaceMethod(String keyword) {
System.out.println("Testing SPI mechanism");
return null;
}
}
Create a META-INF/services directory under the resources directory, and then create a file with the package path.
Execution Result:
SPI Analysis:
Load Method:
/**
* Creates a new service loader for the given service type, using the
* current thread's {@linkplain java.lang.Thread#getContextClassLoader
* context class loader}.
*
* <p> An invocation of this convenience method of the form
* <blockquote><pre>
* ServiceLoader.load(<i>service</i>)</pre></blockquote>
*
* is equivalent to
* <blockquote><pre>
* ServiceLoader.load(<i>service</i>,
* Thread.currentThread().getContextClassLoader())</pre></blockquote>
*
* @param <S> the class of the service type
* @param service
* The interface or abstract class representing the service
* @return A new service loader
*/
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
Load Method Analysis:
/**
* Creates a new service loader for the given service type and class
* loader.
*
* @param <S> the class of the service type
* @param service
* The interface or abstract class representing the service
* @param loader
* The class loader to be used to load provider-configuration files
* and provider classes, or <tt>null</tt> if the system class
* loader (or, failing that, the bootstrap class loader) is to be
* used
* @return A new service loader
*/
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader) {
return new ServiceLoader<>(service, loader);
}
A ServiceLoader object is constructed.
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
Understanding the Constructor:
Reload Method:
/**
* Clear this loader's provider cache so that all providers will be
* reloaded.
*
* <p> After invoking this method, subsequent invocations of the {@link
* #iterator() iterator} method will lazily look up and instantiate
* providers from scratch, just as is done by a newly-created loader.
* <p> This method is intended for use in situations in which new providers
* can be installed into a running Java virtual machine.
*/
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
The reload method constructs an object calledLazyIterator.HasNext Method:
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
HasNextService Method:
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = 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;
}
Understanding the HasNextService Method:

Next Method:
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);
}
}
NextService Method:
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(); // This cannot happen
}
Understanding the NextService Method:
This is the process of the SPI mechanism.Class Loader’s Cast Method:
Defects of the SPI Mechanism:1.It cannot load on demand; it needs to traverse all implementations and instantiate them, and only then can we find the implementation we need. If we do not want to use certain implementation classes, or if certain class instantiation is time-consuming, they are still loaded and instantiated, which causes waste.2.The way to obtain a specific implementation class is not flexible enough; it can only be obtained in an Iterator form and cannot be retrieved based on a specific parameter.3.Using instances of the ServiceLoader class in multiple concurrent threads is not safe.Recently, I have become fascinated with Java bytecode, perhaps due to my interest. I found that it is like pulling a thread that affects the whole body; everything I had glanced at before now seems to connect. Thus, I review the previous material while looking at new things. Suddenly, this SPI mechanism came to mind. I gradually discovered that knowledge is learned repeatedly. Things are used repeatedly. What is it for? For understanding. Now, I feel that understanding is paramount.