Mastering Java and Spring SPI: From Basics to Advanced Implementation

This article explains Java's built‑in Service Provider Interface (SPI) mechanism, demonstrates how to create and load SPI implementations with ServiceLoader, and then shows how Spring extends SPI using spring.factories with detailed code examples and source‑code analysis.

Architect's Guide
Architect's Guide
Architect's Guide
Mastering Java and Spring SPI: From Basics to Advanced Implementation

Java SPI (Service Provider Interface) is a built‑in discovery mechanism that enables framework extensions by loading implementations of a given interface from META-INF/services files on the classpath. The article first introduces the core concept and references a previous deep dive.

Java SPI Implementation

The java.util.ServiceLoader class scans the classpath for files named after the fully qualified interface name under META-INF/services and loads the listed implementation classes.

Creating a Dynamic Interface

public interface VedioSPI {
    void call();
}

Implementation 1

public class Mp3Vedio implements VedioSPI {
    @Override
    public void call() {
        System.out.println("this is mp3 call");
    }
}

Implementation 2

public class Mp4Vedio implements VedioSPI {
    @Override
    public void call() {
        System.out.println("this is mp4 call");
    }
}

Place a file named com.skywares.fw.juc.spi.VedioSPI in META-INF/services containing the fully qualified names of the implementations.

SPI directory structure
SPI directory structure

Testing the Java SPI

public class VedioSPITest {
    public static void main(String[] args) {
        ServiceLoader<VedioSPI> loader = ServiceLoader.load(VedioSPI.class);
        loader.forEach(VedioSPI::call);
    }
}

The output shows both implementations being invoked, confirming that ServiceLoader successfully discovers and iterates over the services.

Java SPI output
Java SPI output

Source Code Analysis of Java SPI

The ServiceLoader class implements Iterable and uses an internal LazyIterator to create iterator instances. All service configuration files reside in the immutable META-INF/services directory.

ServiceLoader’s iterator delegates to LazyIterator for lazy loading.

Each service interface requires a separate configuration file.

Limitations of Java SPI include the need to traverse all services and the mandatory placement of configuration files under META-INF/services.

Spring SPI

Spring adopts the same SPI idea but uses a single spring.factories file to map multiple interfaces to their implementations, enhancing extensibility without modifying Spring’s core.

Defining an Interface

public interface DataBaseSPI {
    void getConnection();
}

Implementations

// DB2 implementation
public class DB2DataBase implements DataBaseSPI {
    @Override
    public void getConnection() {
        System.out.println("this database is db2");
    }
}

// MySQL implementation
public class MysqlDataBase implements DataBaseSPI {
    @Override
    public void getConnection() {
        System.out.println("this is mysql database");
    }
}

Steps to enable Spring SPI:

Create spring.factories under META-INF.

Add the mapping:

com.skywares.fw.juc.springspi.DataBaseSPI = com.skywares.fw.juc.springspi.DB2DataBase, com.skywares.fw.juc.springspi.MysqlDataBase

Multiple implementations are separated by commas.

Testing Spring SPI

public class SpringSPITest {
    public static void main(String[] args) {
        List<DataBaseSPI> spis = SpringFactoriesLoader.loadFactories(DataBaseSPI.class, Thread.currentThread().getContextClassLoader());
        for (DataBaseSPI spi : spis) {
            spi.getConnection();
        }
    }
}
Spring SPI output
Spring SPI output

Key differences:

Java SPI: one configuration file per interface, all files under services directory.

Spring SPI: a single spring.factories file holds multiple interface‑implementation mappings, using fully qualified interface names as keys and comma‑separated implementation class names as values.

Spring SPI Source Code Insights

The method loadFactoryNames parses spring.factories to retrieve fully qualified implementation class names. It enumerates all META-INF/spring.factories resources across the classpath, merges entries, and uses reflection to instantiate each class.

loadFactoryNames flow
loadFactoryNames flow

Overall, Spring’s SPI builds on Java’s ServiceLoader but simplifies configuration and supports multiple implementations per interface within a single file, addressing some of Java SPI’s drawbacks.

Reference: https://juejin.cn/post/7132742686099898398

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backend DevelopmentspringDependency InjectionSPIServiceLoaderSpring Factories
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.