Understanding and Implementing Java SPI (Service Provider Interface) with Examples
This article explains the Java SPI mechanism, demonstrates how to create service interfaces and implementations, shows how to configure META-INF/services and spring.factories files, and compares Java's built‑in SPI with Spring's optimized SPI approach, providing complete code samples and analysis.
SPI (Service Provider Interface) is a built‑in Java mechanism that enables framework extensions and component replacement by discovering implementations of a given interface at runtime, commonly used in frameworks such as Dubbo, Spring, and JDBC.
The Java SPI works via java.util.ServiceLoader , which scans the classpath and JAR files for META-INF/services/ entries named after the fully qualified interface name and loads the listed implementation classes.
Java SPI Implementation
First, define a dynamic interface:
public interface VedioSPI {
void call();
}Then provide two implementations:
public class Mp3Vedio implements VedioSPI {
@Override
public void call() {
System.out.println("this is mp3 call");
}
} 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 under META-INF/services/ containing the fully qualified names of the implementations.
Related Test
public class VedioSPITest {
public static void main(String[] args) {
ServiceLoader
serviceLoader = ServiceLoader.load(VedioSPI.class);
serviceLoader.forEach(t -> t.call());
}
}The test demonstrates that ServiceLoader locates the implementations and invokes their call() methods.
Source Analysis
The core of Java SPI is the ServiceLoader class, which implements Iterable and uses an internal LazyIterator to lazily instantiate providers. All provider configuration files reside in META-INF/services/ , and the directory name is fixed.
Limitations of Java SPI include the need to traverse all providers and the requirement that configuration files be placed in the specific directory.
Spring SPI
Spring adopts the same design idea but extends it using a spring.factories file, allowing multiple implementations of different interfaces to be declared in a single configuration file, improving manageability.
Spring Example
Define an interface:
public interface DataBaseSPI {
void getConnection();
}Provide two implementations:
public class DB2DataBase implements DataBaseSPI {
@Override
public void getConnection() {
System.out.println("this database is db2");
}
} public class MysqlDataBase implements DataBaseSPI {
@Override
public void getConnection() {
System.out.println("this is mysql database");
}
}Add a spring.factories file under META-INF/ with the entry:
com.skywares.fw.juc.springspi.DataBaseSPI = com.skywares.fw.juc.springspi.DB2DataBase, com.skywares.fw.juc.springspi.MysqlDataBaseTest class:
public class SpringSPITest {
public static void main(String[] args) {
List
dataBaseSPIs = SpringFactoriesLoader.loadFactories(DataBaseSPI.class, Thread.currentThread().getContextClassLoader());
for (DataBaseSPI dataBaseSPI : dataBaseSPIs) {
dataBaseSPI.getConnection();
}
}
}The output shows that Spring loads both implementations via the single spring.factories file, simplifying configuration compared to Java SPI.
Conclusion
Both Java and Spring provide SPI mechanisms for decoupled service loading; Spring’s approach builds on Java’s design while offering a more compact configuration format and additional optimizations.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.