Understanding Java SPI with Practical Code Examples

Java SPI Overview

Java SPI (Service Provider Interface) is a built-in mechnaism for discovering and loading service implementations dynamically. Instead of hardcoding specific implementation classes, SPI allows you to define a service interface and have multiple providers supply their own implementations. The ServiceLoader class, part of the JDK, loads these services at runtime based on configuration files.

SPI Rules

  1. Create a file under META-INF/services/ named after the fully qualified interface name. The file contains one or more lines, each specifying the fully qualified class name of an implementation.
  2. Use java.util.ServiceLoader to load the implementations at runtime.
  3. If the implementation classes are packaged in a JAR, that JAR must be on the classpath.
  4. Each implementation class must have a public no-argument constructor.

Common Usage Scenarios

JDBC

Before JDBC 4.0, developers had to explicitly load database drivers using Class.forName("driver.class.name"). Since JDBC 4, the driver discovery relies on SPI. Drivers declare themselves via the META-INF/services/java.sql.Driver file, allowing the DriverManager to locate and load them automatically.

Apache Commons Logging

This logging facade uses SPI to find the actual logging implementation. Providers place a file META-INF/services/org.apache.commons.logging.LogFactory that points to the factory class. Commons Logging reads that file to instantiaet the correct factory.

Code Example

1. Service Interface

package com.example.spi.service;

public interface Greeting {
    String speak();
}

2. Implementations

ChineseGreeting.java

package com.example.spi.impl;

import com.example.spi.service.Greeting;

public class ChineseGreeting implements Greeting {
    @Override
    public String speak() {
        return "Ni Hao";
    }
}

EnglishGreeting.java

package com.example.spi.impl;

import com.example.spi.service.Greeting;

public class EnglishGreeting implements Greeting {
    @Override
    public String speak() {
        return "Hello";
    }
}

3. SPI Configuration File

Create the directory META-INF/services/ and inside it a file named com.example.spi.service.Greeting. The content:

com.example.spi.impl.ChineseGreeting
com.example.spi.impl.EnglishGreeting

4. Client Code (Loading the Service)

package com.example.client;

import com.example.spi.service.Greeting;
import java.util.ServiceLoader;

public class SpiClient {
    public static void main(String[] args) {
        ServiceLoader<Greeting> loader = ServiceLoader.load(Greeting.class);

        for (Greeting greeting : loader) {
            System.out.println(greeting.speak());
        }
    }
}

5. Output

Ni Hao
Hello

Extension Example

Suppose you want to add a Korean greeting. You create a new module:

  • KoreanGreeting.java implementing Greeting.
  • A META-INF/services/com.example.spi.service.Greeting file containing com.example.spi.impl.korean.KoreanGreeting.
  • Package the module as a JAR and place it on the classpath.

The client code remains unchanged and will automatically detect the new implementation.

Tags: java SPI ServiceLoader Module Design

Posted on Thu, 18 Jun 2026 17:09:31 +0000 by indigobanana