---
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 çä¸ä¸ªæ¥å¿é¨é¢ï¼æ¥å£ï¼ï¼å
¶å
·ä½å®ç°æå ç§ï¼æ¯å¦ï¼LogbackãLog4jãLog4j2 ççï¼èä¸è¿å¯ä»¥åæ¢ï¼å¨åæ¢æ¥å¿å
·ä½å®ç°çæ¶åæä»¬æ¯ä¸éè¦æ´æ¹é¡¹ç®ä»£ç çï¼åªéè¦å¨ 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 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 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 getProviders() {
return providers;
}
}
```
å
³é®ä¿¡æ¯åºæ¬å·²ç»éè¿ä»£ç 注éæè¿°åºæ¥äºï¼
主è¦çæµç¨å°±æ¯ï¼
1. éè¿ URL å·¥å
·ç±»ä» jar å
ç `/META-INF/services` ç®å½ä¸é¢æ¾å°å¯¹åºçæä»¶ï¼
2. 读åè¿ä¸ªæä»¶çåç§°æ¾å°å¯¹åºç spi æ¥å£ï¼
3. éè¿ `InputStream` æµå°æä»¶éé¢çå
·ä½å®ç°ç±»çå
¨ç±»å读ååºæ¥ï¼
4. æ ¹æ®è·åå°çå
¨ç±»åï¼å
å¤æè· spi æ¥å£æ¯å¦ä¸ºåä¸ç±»åï¼å¦ææ¯çï¼é£ä¹å°±éè¿åå°çæºå¶æé 对åºçå®ä¾å¯¹è±¡ï¼
5. å°æé åºæ¥çå®ä¾å¯¹è±¡æ·»å å° `Providers` çå表ä¸ã
## æ»ç»
å
¶å®ä¸é¾åç°ï¼SPI æºå¶çå
·ä½å®ç°æ¬è´¨ä¸è¿æ¯éè¿åå°å®æçãå³ï¼**æä»¬æç
§è§å®å°è¦æ´é²å¯¹å¤ä½¿ç¨çå
·ä½å®ç°ç±»å¨ `META-INF/services/` æä»¶ä¸å£°æã**
å¦å¤ï¼SPI æºå¶å¨å¾å¤æ¡æ¶ä¸é½æåºç¨ï¼Spring æ¡æ¶çåºæ¬åç乿¯ç±»ä¼¼çåå°ãè¿æ Dubbo æ¡æ¶æä¾åæ ·ç SPI æ©å±æºå¶ï¼åªä¸è¿ Dubbo å spring æ¡æ¶ä¸ç SPI æºå¶å
·ä½å®ç°æ¹å¼è·å±ä»¬ä»å¤©å¦å¾è¿ä¸ªæäºç»å¾®çåºå«ï¼ä¸è¿æ´ä½çåç齿¯ä¸è´çï¼ç¸ä¿¡å¤§å®¶éè¿å¯¹ JDK ä¸ SPI æºå¶çå¦ä¹ ï¼è½å¤ä¸éç¾éï¼å 深对å
¶ä»é«æ·±æ¡ççè§£ã
éè¿ SPI æºå¶è½å¤å¤§å¤§å°æé«æ¥å£è®¾è®¡ççµæ´»æ§ï¼ä½æ¯ SPI æºå¶ä¹åå¨ä¸äºç¼ºç¹ï¼æ¯å¦ï¼
1. éåå è½½ææçå®ç°ç±»ï¼è¿æ ·æçè¿æ¯ç¸å¯¹è¾ä½çï¼
2. å½å¤ä¸ª `ServiceLoader` åæ¶ `load` æ¶ï¼ä¼æå¹¶åé®é¢ã