JDK和Spring中SPI的實現原理和區別

JDK和Spring中SPI的實現原理和區別

SPI 簡介

service provider interface JDK內置的中服務發現機制

一種動態替換髮現的機制

JDK和Spring中SPI的實現原理和區別

使用方式

實現方式:

  • 寫service 具體對外提供的接口
public interface DriverService {
String getName();
}
  • 具體的實現,繼承對應的接口
public class JavaDriverImpl implements DriverService {
@Override
public String getName() {
return "java implement";
}
}
  • 編寫META-INF/service 具體的實現類 包名+類名
JDK和Spring中SPI的實現原理和區別

com.chengjue.spi.JavaDriverImpl
  • 編譯jar包對外提供服務

使用方:

  • 引用相關依賴 jar包
  • 使用ServiceLoader加載使用
 public static void main(String[] args) {
ServiceLoader<driverservice> serviceLoader = ServiceLoader.load(DriverService.class);
for (DriverService driverService: serviceLoader){
System.out.println(driverService.getName());
}
}
/<driverservice>

實現原理

我們使用的時候都是使用的ServiceLoader加載服務,下面來看下ServiceLoader是具體怎麼實現的

ServiceLoader是JDK提供的一個util,在java.util包下

我們可以看到他的介紹A simple service-provider loading facility.用來加載服務的

public final class ServiceLoader implements Iterable

我們可以看到他是一個final類型的,不可以被繼承修改,同時實現了Iterable接口,方便我們使用迭代器取出所有的實現類

接下來我們可以看到一個熟悉的常量,這個就是我們在前面定義實現類的路徑

private static final String PREFIX = "META-INF/services/";

下面便是load方法的具體實現

 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實現的。接下來我們去看LazyIterator的構造方法傳入 Class 和ClassLoader。

下面是ServiceLoader的iterator方法實現,這邊會去new一個Iterator,首先在ServiceLoader中有一個provider的緩存,每次操作的時候都會先去緩存中查找,否則採取LazyIterator中去查找。

public Iterator iterator() {
return new Iterator() {
Iterator<map.entry>> 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();
}
};
}
/<map.entry>

下面是LazyIterator的具體處理

 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);

}
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//通過PREFIX(META-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
}
/<boolean>/<boolean>

總結

通過上面的代碼學習,發現其實它根本還是通過反射的方式獲取具體的實現類的實例,我們只是通過SPI定義的方式,將要暴露對外使用的具體實現在META-INF/services/文件下聲明而已。所以對於反射的掌握還是很重要的,無論是spring還是其他的一些框架中經常可以看到反射的使用

在現有框架中的使用

其實瞭解SPI機制是因為最近看SpringBoot代碼的時候發現的,我們知道在SprngBoot中好多的配置和實現都有默認的實現,我們只需要修改部分配置,比如數據庫配置,我們只要在配置文件中寫上對應的url,username,password就可以使用了。其實他這邊用的就是SPI的方式實現的

不過Spring使用的只是和JDK中的原理相同而已。

  • JDK使用的工具類是ServiceLoader
  • Spring中使用的類是SpringFactoriesLoader,在org.springframework.core.io.support包中

區別:

  • 文件路徑不同 spring配置放在 META-INF/spring.factories中
JDK和Spring中SPI的實現原理和區別

  • 具體的實現步驟不一樣,不過原理相同,都是使用的反射機制

Spring中實現

public static  List loadFactories(Class factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<string> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List result = new ArrayList<>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
/<string>

學習就是這樣,舉一反三,如果JDK中的SPI原理搞明白了,再去看Spring中相關的實現就會比較容易理解


分享到:


相關文章: