Mastering SPI: Build Extensible Java, Spring, and Dubbo Applications
This article explains the Service Provider Interface (SPI) mechanism across JDK, Spring, and Dubbo, showing how to create modular, maintainable code through interface definitions, configuration files, and ServiceLoader or ExtensionLoader usage, with detailed examples, code snippets, and analysis of advantages and drawbacks.
What is SPI?
SPI (Service Provider Interface) is an API designed to be implemented or extended by third parties. It enables enabling, extending, or even replacing components within a framework without modifying the original code base. For example, JDBC defines the java.sql.Driver interface, allowing developers to plug in different database drivers such as MySQL or Oracle.
JDK SPI
Example: Define an interface and its implementation, then configure ServiceLoader.
Define interface specification
package com.demo.jdkspi.api;
public interface SayHelloService {
String sayHello(String name);
}Define implementation class
public class SayHelloImpl implements SayHelloService {
public String sayHello(String name) {
return "你好" + name + ",欢迎关注网易云商!";
}
}Configuration file In resources/META-INF/services/com.demo.jdkspi.api.SayHelloService add: com.demo.jdkspi.impl.SayHelloServiceImpl Write test class
public static void main(String[] args) {
ServiceLoader<SayHelloService> loader = ServiceLoader.load(SayHelloService.class);
loader.forEach(s -> System.out.println(s.sayHello("Jack")));
}Running this prints the greeting.
JDK SPI Principle
The mechanism relies on ServiceLoader, which performs lazy loading: the implementation classes are not instantiated until the loader is iterated. The creation flow includes obtaining the thread context ClassLoader, clearing caches, and creating a LazyIterator for iteration.
Spring SPI
Spring Boot Starters use SPI concepts. By creating a custom starter, you can expose a SayHelloService implementation to the Spring IoC container.
Implementation and configuration
public class Greeter implements SayHelloService, InitializingBean {
public String sayHello(String name) { return "你好" + name + ",欢迎关注网易云商!"; }
public void afterPropertiesSet() { System.out.println("网易云商服务加载完毕,欢迎使用!"); }
} @Configuration
public class TestAutoConfiguration {
@Bean
public SayHelloService sayHelloService(){ return new Greeter(); }
}Add spring.factories under resources/META-INF with:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.demo.springspi.TestAutoConfigurationSpring SPI Principle
Spring uses SpringFactoriesLoader to read META-INF/spring.factories, converting entries into BeanDefinition objects that the IoC container manages. The process involves AutoConfigurationImportSelector and ConfigurationClassPostProcessor to load and register configuration classes.
Dubbo SPI
Dubbo enhances the SPI mechanism with named, activated, and adaptive extensions.
Named extension point
@SPI
public interface SayHelloService { String sayHello(String name); } public class SayHelloServiceImpl implements SayHelloService {
public String sayHello(String name) { return "你好" + name + ",欢迎关注网易云商!"; }
}Configure META-INF/dubbo/com.spi.api.dubbo.SayHelloService with:
neteaseSayHelloService=com.spi.impl.dubbo.SayHelloServiceImplActivated extension point
@Activate(group = {Constants.PROVIDER}, order = Integer.MIN_VALUE)
public class LogFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation invocation) {
System.out.println("打印调用日志");
return invoker.invoke(invocation);
}
} @Activate(group = {Constants.PROVIDER}, order = 0)
public class SystemStatusCheckFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
if(!sysEnable()) { throw new RuntimeException("系统未就绪,请稍后再试"); }
System.out.println("系统准备就绪,能正常使用");
return invoker.invoke(invocation);
}
}Add META-INF/dubbo/com.alibaba.dubbo.rpc.Filter with the filter mappings.
Adaptive extension point
@SPI("default")
public interface SimpleAdaptiveExt {
@Adaptive("serviceKey")
void sayHello(URL url, String name);
} public class DefaultExtImp implements SimpleAdaptiveExt {
public void sayHello(URL url, String name) { System.out.println("Hello " + name); }
} public class OtherExtImp implements SimpleAdaptiveExt {
public void sayHello(URL url, String name) { System.out.println("Hi " + name); }
}Configure META-INF/dubbo/com.spi.impl.dubbo.adaptive.SimpleAdaptiveExt with:
default=com.spi.impl.dubbo.adaptive.DefaultExtImp
other=com.spi.impl.dubbo.adaptive.OtherExtImpDubbo SPI Principle
Dubbo’s ExtensionLoader loads extensions from META-INF/dubbo, META-INF/dubbo/internal, and META-INF/services. It caches loaders, instantiates classes via reflection, performs setter‑based dependency injection, and wraps instances with a Wrapper for AOP support. Adaptive extensions are generated at runtime using Javassist to create a proxy that selects the implementation based on URL parameters.
Summary
JDK SPI provides decoupling and lazy loading but lacks conditional loading and dependency injection.
Spring SPI solves the injection issue and supports conditional loading via auto‑configuration.
Dubbo SPI extends JDK SPI with named, activated, and adaptive extensions, supporting IoC, AOP, and on‑demand loading.
Future Outlook: Multi‑Tenant Customization with SPI
In multi‑tenant systems, SPI can manage tenant‑specific customizations by abstracting customization points as interfaces, registering implementations in a central platform, and dynamically loading the appropriate extensions at runtime, ensuring isolation, reuse, and easy maintenance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
NetEase Smart Enterprise Tech+
Get cutting-edge insights from NetEase's CTO, access the most valuable tech knowledge, and learn NetEase's latest best practices. NetEase Smart Enterprise Tech+ helps you grow from a thinker into a tech expert.
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.
