---
title: Java SPI
category: Java
tag:
- Java
head:
- - meta
- name: keywords
content: Java SPI
- - meta
- name: description
content: SPI Service Provider Interface SPI
---
> [Kingshion](https://github.com/jjx0708) JavaGuide [JavaGuide ](https://javaguide.cn/javaguide/contribution-guideline.html)
Java SPI ** IoC **
## SPI
### SPI?
SPI Service Provider Interface
SPI
Java SPI Spring Dubbo

### SPI API
** SPI API **
SPI API

API
SPI
H H
##
SLF4J Simple Logging Facade for Java Java LogbackLog4jLog4j2 Maven pom

SPI
### Service Provider Interface
Java `service-provider-interface` Java Maven Maven deploy
```
service-provider-interface.iml
.idea
.gitignore
misc.xml
modules.xml
workspace.xml
src
edu
jiangxuan
up
spi
Logger.java
LoggerService.java
Main.class
```
`Logger` SPI
```java
package edu.jiangxuan.up.spi;
public interface Logger {
void info(String msg);
void debug(String msg);
}
```
`LoggerService` Java SPI
```java
package edu.jiangxuan.up.spi;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
public class LoggerService {
private static final LoggerService SERVICE = new LoggerService();
private final Logger logger;
private final List loggerList;
private LoggerService() {
ServiceLoader loader = ServiceLoader.load(Logger.class);
List list = new ArrayList();
for (Logger log : loader) {
list.add(log);
}
// LoggerList ServiceProvider
loggerList = list;
if (!list.isEmpty()) {
// Logger
logger = list.get(0);
} else {
logger = null;
}
}
public static LoggerService getService() {
return SERVICE;
}
public void info(String msg) {
if (logger == null) {
System.out.println("info Logger ");
} else {
logger.info(msg);
}
}
public void debug(String msg) {
if (loggerList.isEmpty()) {
System.out.println("debug Logger ");
}
loggerList.forEach(log -> log.debug(msg));
}
}
```
`Main`
```java
package org.spi.service;
public class Main {
public static void main(String[] args) {
LoggerService service = LoggerService.getService();
service.info("Hello SPI");
service.debug("Hello SPI");
}
}
```
> info Logger
> debug Logger
`Logger`
IDEA jar
### Service Provider
`Logger`
`service-provider`
```
service-provider.iml
.idea
.gitignore
misc.xml
modules.xml
workspace.xml
lib
service-provider-interface.jar
|
src
edu
jiangxuan
up
spi
service
Logback.java
META-INF
services
edu.jiangxuan.up.spi.Logger
```
`Logback`
```java
package edu.jiangxuan.up.spi.service;
import edu.jiangxuan.up.spi.Logger;
public class Logback implements Logger {
@Override
public void info(String s) {
System.out.println("Logback info " + s);
}
@Override
public void debug(String s) {
System.out.println("Logback debug " + s);
}
}
```
`service-provider-interface` jar
lib jar

OK

jar JDK
`Logger` `src` `META-INF/services` `edu.jiangxuan.up.spi.Logger` SPI `edu.jiangxuan.up.spi.service.Logback` Logback SPI +
** JDK SPI ServiceLoader **
Java SPI class `META-INF` services list
`service-provider` jar jar maven pom jar maven
###
`java-spi-test`
`Logger` jar jar

Main
```java
package edu.jiangxuan.up.service;
import edu.jiangxuan.up.spi.LoggerService;
public class TestJavaSPI {
public static void main(String[] args) {
LoggerService loggerService = LoggerService.getService();
loggerService.info("");
loggerService.debug("Java SPI ");
}
}
```
> Logback info
> Logback debug Java SPI
jar
jar
> info Logger
> debug Logger
SPI `LoggerService` `service-provider` `Logger` jar SLF4J
Logback service-provider jar (LoggerService) (service-provider)
Java SPI **ServiceLoader**
## ServiceLoader
### ServiceLoader
Java SPI `ServiceLoader` `ServiceLoader`
`ServiceLoader` JDK `package java.util;`
```
A facility to load implementations of a service.
```
JDK ****
`final` `Iterable`
```java
public final class ServiceLoader implements Iterable{ xxx...}
```
`private static final String PREFIX = "META-INF/services/";`
`load` `load`
```java
public static ServiceLoader load(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
public static ServiceLoader load(Class service,
ClassLoader loader) {
return new ServiceLoader(service, loader);
}
private ServiceLoader(Class 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();
}
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
```
`reload()` `LazyIterator`
`ServiceLoader` `Iterable` `iterator` `ServiceLoader` `Provider` `LazyIterator`
```java
public Iterator iterator() {
return new Iterator() {
Iterator knownProviders
= providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
return lookupIterator.hasNext(); // LazyIterator
}
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
return lookupIterator.next(); // LazyIterator
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
```
`LazyIterator`
```java
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction action = new PrivilegedAction() {
public Boolean run() {
return hasNextService();
}
};
return AccessController.doPrivileged(action, acc);
}
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//PREFIXMETA-INF/services/
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;
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction action = new PrivilegedAction() {
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(); // This cannot happen
}
```
`ServiceLoader`
### ServiceLoader
```java
package edu.jiangxuan.up.service;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class MyServiceLoader {
// Class
private final Class service;
// List
private final List providers = new ArrayList();
//
private final ClassLoader classLoader;
//
public static MyServiceLoader load(Class service) {
return new MyServiceLoader(service);
}
//
private MyServiceLoader(Class service) {
this.service = service;
this.classLoader = Thread.currentThread().getContextClassLoader();
doLoad();
}
//
private void doLoad() {
try {
// jar META-INF/services
Enumeration urls = classLoader.getResources("META-INF/services/" + service.getName());
//
while (urls.hasMoreElements()) {
//
URL url = urls.nextElement();
System.out.println("File = " + url.getPath());
//
URLConnection urlConnection = url.openConnection();
urlConnection.setUseCaches(false);
//
InputStream inputStream = urlConnection.getInputStream();
//
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//
String className = bufferedReader.readLine();
while (className != null) {
//
Class clazz = Class.forName(className, false, classLoader);
// Java
if (service.isAssignableFrom(clazz)) {
Constructor