Hello, I’m Wai Wai.
Today, let’s talk about the principles and differences of the SPI mechanism in Java, Spring, and Dubbo.
What is SPI
SPI stands for Service Provider Interface, which is a mechanism for dynamic replacement and discovery, embodying an excellent decoupling concept. SPI allows for flexible separation between interfaces and implementations, enabling API providers to only provide interfaces while third parties implement them. This can be achieved through configuration files, making it common in frameworks and enhancing their extensibility.
In simple terms, SPI is an excellent design concept, with its core being decoupling and ease of extension.
Java SPI Mechanism — ServiceLoader
ServiceLoader is a simple implementation of the SPI mechanism provided by Java. The Java SPI implementation has the following conventions:
-
Files must be placed under the META-INF/services/
directory -
The file name must be the fully qualified name of the interface, and the content must be the fully qualified name of the interface implementation
In this way, the ServiceLoader can load the implementations of the interfaces from the files.
Demo
Step 1: You need an interface and its implementation class
public interface LoadBalance {
}
public class RandomLoadBalance implements LoadBalance{
}
Step 2: Create a file in the META-INF/services/
directory with the fully qualified name of the LoadBalance interface, and the content should be the fully qualified name of RandomLoadBalance


Test class:
public class ServiceLoaderDemo {
public static void main(String[] args) {
ServiceLoader<LoadBalance> loadBalanceServiceLoader = ServiceLoader.load(LoadBalance.class);
Iterator<LoadBalance> iterator = loadBalanceServiceLoader.iterator();
while (iterator.hasNext()) {
LoadBalance loadBalance = iterator.next();
System.out.println("Obtained load balancing strategy:" + loadBalance);
}
}
}
Test result:

At this point, the implementation has been successfully obtained.
In actual framework design, the above test code is usually written into the framework by its authors. For framework users, to customize the LoadBalance implementation and integrate it into the framework, they only need to write the interface implementation and the SPI file.
Implementation Principle
Below is a core piece of code from ServiceLoader

First, obtain a fullName, which is essentially META-INF/services/ fully qualified name of the interface

Then, use ClassLoader to obtain the resource, which is the resource corresponding to the file of the fully qualified name of the interface, and hand it over to the parse
method for parsing

The parse
method reads the content of the file through an IO stream, allowing us to obtain the fully qualified name of the interface implementation.
Subsequently, it uses reflection to instantiate the object, which will not be displayed here.
Thus, it can be seen that the implementation principle of ServiceLoader is relatively simple. In summary, it reads the content of the META-INF/services/ fully qualified name of the interface
file through an IO stream and then instantiates the object via reflection.
Advantages and Disadvantages
Due to the simplicity of the Java SPI mechanism, it also has some drawbacks.
The first drawback is resource waste. Although there is only one implementation class in the example, in reality, there may be many implementation classes, and Java’s SPI will instantiate all of them at once, regardless of whether they are actually used, leading to unnecessary resource waste.
The second drawback is the inability to distinguish specific implementations. Among many implementation classes, how to determine which implementation to use? If one needs to identify which to use, it can only rely on the design of the interface itself, such as designing it as a strategy interface or incorporating priority into the design. However, regardless of the design, the framework author must write code to make this judgment.
In summary, ServiceLoader cannot achieve on-demand loading or retrieval of a specific implementation.
Usage Scenarios
Although ServiceLoader has some drawbacks, it still has usage scenarios, such as:
-
No need to select a specific implementation; all loaded implementations need to be utilized -
While a specific implementation needs to be selected, this can be resolved through the design of the interface
Spring SPI Mechanism — SpringFactoriesLoader
Spring is familiar to us, and it also provides an SPI implementation called SpringFactoriesLoader.
The conventions for Spring’s SPI mechanism are as follows:
-
Configuration files must be in the META-INF/
directory, and the file name must be spring.factories -
The file content is key-value pairs, where a key can have multiple values separated by commas, and both keys and values must be fully qualified class names. Keys and values may have no relationship or may have an implementation relationship.
Thus, it can be seen that Spring’s SPI mechanism differs from Java’s in terms of both file names and content conventions.
Demo
Create a spring.factories file in the META-INF/
directory, with LoadBalance as the key and RandomLoadBalance as the value


Testing:
public class SpringFactoriesLoaderDemo {
public static void main(String[] args) {
List<LoadBalance> loadBalances = SpringFactoriesLoader.loadFactories(LoadBalance.class, MyEnableAutoConfiguration.class.getClassLoader());
for (LoadBalance loadBalance : loadBalances) {
System.out.println("Obtained LoadBalance object:" + loadBalance);
}
}
}
Running result:

Successfully obtained the implementation object.
Core Principle
Below is a core piece of code from SpringFactoriesLoader

It can be seen that it is quite similar to Java’s implementation, except that it reads the content of the spring.factories file in the META-INF/
directory and parses it into key-value pairs.
Usage Scenarios
Spring’s SPI mechanism is widely used internally, especially in Spring Boot, where many extension points are implemented through the SPI mechanism. Here are two examples:
1. Auto-Configuration
In versions of Spring Boot before 3.0, auto-configuration was loaded using SpringFactoriesLoader.

However, after Spring Boot 3.0, it no longer uses SpringFactoriesLoader but reads from META-INF/spring/
directory’s org.springframework.boot.autoconfigure.AutoConfiguration.imports
file.

As for how it reads, it can be guessed that it is quite similar to the way the above SPI mechanism reads, just with different file paths and names.
2. PropertySourceLoader Loading
PropertySourceLoader is used to parse application configuration files and is an interface.

Spring Boot provides PropertiesPropertySourceLoader and YamlPropertySourceLoader as the two implementations for parsing properties and YAML file formats, respectively.
Spring Boot utilizes the SPI mechanism when loading PropertySourceLoader.

Comparison with Java SPI Mechanism
Firstly, Spring’s SPI mechanism simplifies Java’s SPI mechanism. In Java’s SPI, each interface requires a corresponding file, whereas Spring’s SPI mechanism only requires a single spring.factories file.
Secondly, regarding content, Java’s SPI mechanism requires the file contents to be the implementation classes of the interfaces, while Spring’s SPI does not require any relationship between key-value pairs, making it more flexible.
The third point is that Spring’s SPI mechanism provides a method for obtaining class qualified names, loadFactoryNames
, which Java’s SPI mechanism lacks. By obtaining the class qualified names through this method, these classes can be injected into the Spring container, loading these Beans instead of just using reflection.
However, Spring’s SPI also does not implement the ability to obtain a specific implementation class, so to find a specific implementation class, one still relies on the design of the interface.
Thus, it can be noted that PropertySourceLoader is essentially a strategy interface, and as noted, when the configuration file is in properties format, it can find the PropertiesPropertySourceLoader object to parse the configuration file.
Dubbo SPI Mechanism — ExtensionLoader
ExtensionLoader is the implementation class of Dubbo’s SPI mechanism. Each interface has its own ExtensionLoader instance, similar to Java’s SPI mechanism.
Likewise, Dubbo’s SPI mechanism has the following conventions:
-
The interface must be annotated with @SPI
-
Configuration files can be placed in META-INF/services/
,META-INF/dubbo/internal/
,META-INF/dubbo/
, andMETA-INF/dubbo/external/
. The file name is also the fully qualified name of the interface -
The content is key-value pairs, where the key is a short name (similar to the name of a Bean in Spring), and the value is the fully qualified name of the implementation class
Demo
First, add the @SPI
annotation to the LoadBalance interface
@SPI
public interface LoadBalance {
}
Then modify the content of the configuration file during Java’s SPI mechanism test to key-value pairs, as Dubbo’s SPI mechanism can also read files from the META-INF/services/
directory, so the file is not rewritten
random=com.sanyou.spi.demo.RandomLoadBalance
Test class:
public class ExtensionLoaderDemo {
public static void main(String[] args) {
ExtensionLoader<LoadBalance> extensionLoader = ExtensionLoader.getExtensionLoader(LoadBalance.class);
LoadBalance loadBalance = extensionLoader.getExtension("random");
System.out.println("Obtained object corresponding to random key:" + loadBalance);
}
}
Using the getExtension
method of ExtensionLoader, by passing in the short name, one can precisely find the implementation class corresponding to that short name.
Thus, it can be seen that Dubbo’s SPI mechanism solves the previous issue of not being able to obtain a specific implementation class.
Test result:

The Dubbo SPI mechanism not only solves the problem of not being able to obtain a specific implementation class but also provides many additional features, which are widely used internally in Dubbo. Next, we’ll elaborate on them.
Dubbo Core Mechanism
1. Adaptive Mechanism
Adaptive means that based on parameters, the specific target class is dynamically selected at runtime and executed.
Each interface can have only one adaptive class, which can be obtained through the ExtensionLoader‘s getAdaptiveExtension
method. This object can find the target implementation class based on specific parameters at runtime and call the methods of the target object.
For example, if the above LoadBalance has an adaptive object, then after obtaining this adaptive object, if the key random
is passed in during runtime, this adaptive object will find the implementation class corresponding to random
and call its methods. If another key is dynamically passed in, it will route to another implementation class.
Adaptive classes can be generated in two ways: the first is to specify it yourself by adding the @Adaptive
annotation to the implementation class of the interface, making it the adaptive implementation class.
@Adaptive
public class RandomLoadBalance implements LoadBalance{
}
In addition to self-specification, Dubbo can also dynamically generate an adaptive class based on certain conditions, which is a complex process and will not be elaborated here.
The adaptive mechanism is widely used in Dubbo, often being automatically generated. If you are unaware of Dubbo’s adaptive mechanism, you might not know why the code can reach certain points while reading the source code.
2. IOC and AOP
When it comes to IOC and AOP, people often think of Spring. However, IOC and AOP are not unique to Spring; Dubbo also implements these functionalities but in a lightweight manner.
2.1. Dependency Injection
Dubbo’s dependency injection is carried out through setter injection, where the injected object is by default the adaptive object mentioned above, and can be injected with Spring Beans in a Spring environment.
public class RoundRobinLoadBalance implements LoadBalance {
private LoadBalance loadBalance;
public void setLoadBalance(LoadBalance loadBalance) {
this.loadBalance = loadBalance;
}
}
In the above code, the RoundRobinLoadBalance has a setLoadBalance method with a LoadBalance parameter. When creating RoundRobinLoadBalance, in a non-Spring environment, Dubbo will find the LoadBalance adaptive object and inject it through reflection.
This method is also common in Dubbo, as seen in the following scenario:

In the RegistryProtocol, a Protocol will be injected, which is actually an adaptive object.
2.2. Interface Callback
Dubbo also provides some callback functionalities similar to Spring’s interfaces. For example, if your class implements the Lifecycle interface, it will call several methods during creation or destruction.

After a certain version of Dubbo 3.x, Dubbo provides more interface callbacks, such as ExtensionPostProcessor, ExtensionAccessorAware, which are very similar to Spring’s and serve similar purposes.
2.3. Automatic Wrapping
Automatic wrapping is essentially the implementation of AOP functionality, proxying the target object, and this AOP functionality is enabled by default.
In the implementations of SPI interfaces in Dubbo, there is a special class known as the Wrapper class, whose role is to implement AOP.
The only criterion for identifying a Wrapper class is that it must have a constructor with a single parameter, and the parameter type must be the type of the interface, as shown in the following code:
public class RoundRobinLoadBalance implements LoadBalance {
private final LoadBalance loadBalance;
public RoundRobinLoadBalance(LoadBalance loadBalance) {
this.loadBalance = loadBalance;
}
}
Thus, RoundRobinLoadBalance is a Wrapper class.
When obtaining the target object RandomLoadBalance via random
, what is actually retrieved is the RoundRobinLoadBalance object, which internally references the RandomLoadBalance.
Testing
Add to the configuration file:
roundrobin=com.sanyou.spi.demo.RoundRobinLoadBalance
Test result:

The result shows that although random
was specified, the actual object obtained was RoundRobinLoadBalance, which internally referenced RandomLoadBalance.
If there are many wrapper classes, they will form a chain of responsibility, one wrapping another.
Thus, Dubbo’s AOP is implemented differently from Spring’s AOP. Spring’s AOP is based on dynamic proxies, while Dubbo’s AOP is considered static proxies, as Dubbo automatically assembles this proxy, forming a chain of responsibility.
At this point, we already know that there are two types of implementations for Dubbo’s SPI interface:
-
Adaptive Classes -
Wrapper Classes
In addition to these two types, there is also a default class, which is the implementation class corresponding to the value of the @SPI
annotation. For example:
@SPI("random")
public interface LoadBalance {
}
In this case, the key random
corresponds to the default implementation, which can be obtained through the getDefaultExtension
method.
3. Automatic Activation
Automatic activation refers to dynamically selecting a batch of implementation classes based on your input parameters.
Implementation classes that can be automatically activated need to be annotated with Activate
. This introduces another classification of implementation classes.
@Activate
public interface RandomLoadBalance {
}
At this point, RandomLoadBalance belongs to the class that can be automatically activated.
The method to obtain automatically activated classes is getActivateExtension
, allowing dynamic selection of a batch of implementation classes based on the input parameters.
The automatic activation mechanism is a core usage scenario in Dubbo, particularly in the Filter chain.
Filter is an extension point in Dubbo that can intercept requests before they are initiated or after responses are obtained, serving a role similar to Spring MVC’s HandlerInterceptor.

As shown, there are many implementations of Filter, so to distinguish whether a Filter implementation acts on the provider or consumer side, the automatic activation mechanism can be used to dynamically select a batch of Filter implementations based on input parameters. For instance, the ConsumerContextFilter is a Filter that operates on the consumer side.
Conclusion
From the analysis above, it can be seen that the core principle of implementing the SPI mechanism is to read the content of specified files through IO streams, parse it, and then add some unique features.
In summary, Java’s SPI implementation is relatively simple and lacks additional functionalities; Spring, benefiting from its IOC and AOP capabilities, has not implemented a too complex SPI mechanism, merely simplifying and optimizing Java’s; however, Dubbo’s SPI mechanism has implemented many features to meet its framework’s requirements, integrating both IOC and AOP functionalities into the SPI mechanism, as well as providing adaptive injection and automatic activation features.
Well, that’s all for the technical part of this article.
The following section is called [Random Thoughts], in which I occasionally share some life-related matters after the technical articles, which have nothing to do with technology. I understand it may seem abrupt, but I enjoy it as it adds a touch of ordinary life to a regular blogger.
Random Thoughts
Recently, I’ve been looking at cars with Max. We’re both complete novices, knowing nothing at all, so basically, we watch videos and make plans at home during weekdays, then go to see the cars and schedule test drives on weekends, leaving us utterly exhausted.
Through this process, I’ve realized that buying a car is almost identical to the experience of buying a computer.
“Entered with a budget of three thousand, and ended up spending ninety-eight thousand.”
Why do I say that?
Because I initially knew nothing, and based on rankings on certain car apps, I randomly selected a few models that looked good to me and listed them in my “cars to see” spreadsheet.
As we did more research, the cars in the spreadsheet were sorted by price, showing a range from 170,000 to 310,000, which indirectly proves that we are indeed novices.
However, we have basically researched all the cars on the list online and have also seen the real cars in the dealerships.
The experience between a 170,000 and a 310,000 car is indeed not on the same dimension. I initially just wanted to take a look at the Wanjie M7, but once I sat in the display car, everything felt just right, and apart from the price, I was extremely satisfied.
I have watched almost all the videos about the Wanjie Intelligent Driving Edition; it’s truly amazing and completely resonates with my “heart”. Huawei is truly leading the way in intelligent driving, from hardware to software.
The car’s system, supported by Harmony, is so smooth that it doesn’t feel like a powerful product. At least among the brands I’ve seen, apart from the Lynk & Co 08’s Meizu system that can barely compete, the systems of other brands are almost overwhelmingly inferior.
Oh, by the way, while researching the M7, I selected some models and colors for comparison in the official mini program and discovered a “leading ahead” bug:
However, when I shared this image, everyone saw “Huawei Authorized Experience Store” and “Deposit” and immediately thought: Did I snag a new phone, the Mate 60 Pro, or did I get the X5?
Even though the error message contained the keyword “carInfo,” people would assume this was a shopping cart, and very few would think this was a reservation for a car “backed by Huawei,” which is the awkward situation Wanjie cars find themselves in the market now.
Lastly, let me show you a scene I saw in the Huawei showroom:
Inside the entire store, all the customers were gathered around the “Mate 60 Pro” and “X5” demo units.
The last time I saw such a scene was in an Apple store.
The national attention towards Huawei is genuinely not exaggerated.
·············· END ··············
Recommended 👍:A Poor Pagination, Hit Three Pitfalls!
Recommended 👍:An Improved Version of the Snowflake Algorithm, It’s Yours.
Recommended 👍:Interviewer: How Many Requests Can a Spring Boot Project Handle? (Beware of Pitfalls)
Recommended 👍:Bro, Don’t Try to Show Off in Business Code.
Recommended 👍:A Regular Programmer’s Bumpy but Shining Decade.
Hello, I’m Wai Wai. I have never worked in a top-tier company, started a business, written a book, nor am I a technical expert, so I don’t have any impressive titles.
Back in the college entrance exam, I randomly got into a second-tier university’s computer science major. It was purely accidental that I entered the programming field, and thus began my incredibly fortunate journey as a programmer.
Speaking of my programming journey, it’s quite interesting. You canclick the blue text to view my programming journey.