Java Service Provider Interface

Last update: 2024-10-01 22:56:33 +0800 +0800
tags: Java | Spi
Available translations: 日本語 / 中文(简体) / 中文(繁體)

Java SPI separates a service contract from one or more providers and from the consumer that selects them. The consumer depends on the service type and uses ServiceLoader, so providers can be added at deployment without an application dependency on concrete classes.

Classpath Services #

Before modules, a provider JAR listed implementations in META-INF/services/<fully.qualified.ServiceName>. The UTF-8 file has one provider class name per line; blank lines and # comments are allowed. Each provider must be accessible to the loader used for discovery and offer a public no-argument constructor.

java
ServiceLoader<Codec> codecs = ServiceLoader.load(Codec.class);
for (Codec codec : codecs) {
    if (codec.supports(mediaType)) return codec;
}

Discovery and provider instantiation are lazy. Iterating, streaming providers, or calling Provider.get() can load and initialize a provider, so malformed configuration, missing dependencies, inaccessible types, or failed constructors surface as ServiceConfigurationError. Treat provider code as deployment-supplied code, select an explicit class loader in containers or plugin systems, and fail with diagnostics at a controlled startup boundary.

Module Services #

With JPMS, the consumer module declares uses com.example.Codec; a provider declares provides com.example.Codec with com.example.JsonCodec. Module resolution accounts for service providers as defined by the module-system rules. A provider class need not be exported merely for ServiceLoader; a named-module provider can have a public no-argument constructor or a public static provider method, subject to the service declaration constraints. ServiceLoader.load(service, loader) follows the supplied loader’s visibility, whereas the module-aware overloads operate from a module layer. The loader and layer are part of the service boundary, not incidental details.

Classpath configuration remains supported for unnamed and automatic modules, which enables migration. Avoid publishing both mechanisms without testing the resolved provider set, because duplicates and ordering assumptions make selection non-portable.

JDBC as an Evolution Example #

Early JDBC driver usage often called Class.forName("vendor.Driver") to trigger a driver’s static registration with DriverManager. JDBC 4 introduced service-provider loading for java.sql.Driver, allowing compatible driver JARs to be discovered from META-INF/services/java.sql.Driver. Modern applications should still configure the intended driver and connection properties explicitly; automatic discovery removes a bootstrap call, not database authentication or version compatibility concerns.

References #

Comments

Read or comment on GitHub (sign-in required to comment)