跳到主要内容

spi 入个门

dolhpin scheduler 里大量使用spi进行插件化解藕, 这里顺便了解下spi.

文档

bealdung Java Service Provider Interface

https://www.baeldung.com/java-spi

spi是java6提供的原生特性, 用于加载指定的interface的实现类.

Java 6 has introduced a feature for discovering and loading implementations matching a given interface: Service Provider Interface (SPI).

  • Service Provider

SPI的实现格式要求, META-INF/services里放置了spi实现的名称.

A specific implementation of the SPI. The Service Provider contains one or more concrete classes that implement or extend the service type.

A Service Provider is configured and identified through a provider configuration file which we put in the resource directory META-INF/services. The file name is the fully-qualified name of the SPI and its content is the fully-qualified name of the SPI implementation.

The Service Provider is installed in the form of extensions, a jar file which we place in the application classpath, the Java extension classpath or the user-defined classpath.

  • service loader

At the heart of the SPI is the ServiceLoader class. This has the role of discovering and loading implementations lazily. It uses the context classpath to locate provider implementations and put them in an internal cache.

demo, 看个例子大概就清楚如何使用了

First, we invoke the static factory method load() to get an instance of ServiceLoader:

ServiceLoader<ExchangeRateProvider> loader = ServiceLoader .load(ExchangeRateProvider.class);

And then we invoke the iterate() method to search and retrieve all available implementations.

Iterator<ExchangeRateProvider> = loader.iterator();

The search result is cached so we can invoke the ServiceLoader.reload() method in order to discover newly installed implementations:

Iterator<ExchangeRateProvider> = loader.reload();

文件配置, spi都是ExchangeRateProvider, 但是具体的实现有不同的名字, 在配置文件里指定.

In order to be discovered, we create a provider configuration file:

META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider

The content of the file is the fully qualified class name of the SPI implementation:

com.baeldung.rate.impl.YahooFinanceExchangeRateProvider

相同实现的spi provider有很多, 从中遍历得到匹配的, 然后可以直接使用.

public final class ExchangeRate {

private static final String DEFAULT_PROVIDER = "com.baeldung.rate.spi.YahooFinanceExchangeRateProvider";

//All providers
public static List<ExchangeRateProvider> providers() {
List<ExchangeRateProvider> services = new ArrayList<>();
ServiceLoader<ExchangeRateProvider> loader = ServiceLoader.load(ExchangeRateProvider.class);
loader.forEach(services::add);
return services;
}

//Default provider
public static ExchangeRateProvider provider() {
return provider(DEFAULT_PROVIDER);
}

//provider by name
public static ExchangeRateProvider provider(String providerName) {
ServiceLoader<ExchangeRateProvider> loader = ServiceLoader.load(ExchangeRateProvider.class);
Iterator<ExchangeRateProvider> it = loader.iterator();
while (it.hasNext()) {
ExchangeRateProvider provider = it.next();
if (providerName.equals(provider.getClass().getName())) {
return provider;
}
}
throw new ProviderNotFoundException("Exchange Rate provider " + providerName + " not found");
}
}