š This might be useful for you community
š± One-on-one communication/interview booklet/resume optimization/job hunting advice, welcome to join “Yudao Rapid Development Platform” knowledge planet. Below are some materials provided by the planet:
“Project Practice (Video)”: Learn from the book, practice in the project“Practice” “High-Frequency Interview Questions in the Internet”: Learning from resumes, spring blossoms “Architecture x System Design”: Crushing and mastering high-frequency interview scenarios “Advanced Java Learning Guide”: Systematic learning of mainstream technology stack in the Internet “Must-Read Java Source Code Column”: Knowing why, knowing the reason
šThis might be a useful open-source project for you
Domestic Star breaking 10w+, the front end includes management background + WeChat mini program, the back end supports monolithic and microservice architectures.
Features include RBAC permissions, SaaS multi-tenancy, data permissions, e-commerce, payment, workflow, large screen reports, WeChat public accounts, CRM, etc.:
Boot Repository: https://gitee.com/zhijiantianya/ruoyi-vue-pro Cloud Repository: https://gitee.com/zhijiantianya/yudao-cloud Video Tutorial: https://doc.iocoder.cn [Domestic First Batch] Supports JDK 21 + SpringBoot 3.2.0, JDK 8 + Spring Boot 2.7.18 dual versions
-
1. Introduction -
2. Define Interface -
3. Service Implementation -
4. Service Discovery -
5. Principle -
6. Application -
7. Summary

Having memorized enough stock phrases, I believe everyone has heard a term, SPI Extension.
Some interviewers like to ask this question, How is SpringBoot’s auto-configuration implemented?
Basically, if you mention that it is based on the Spring SPI extension mechanism and refer to the spring.factories
file and EnableAutoConfiguration
, then you have answered the question almost correctly.
Just like four or five years ago, when I was asked this question in an interview, the words SPI dynamic extension mechanism coming out of my mouth left the interviewer stunned. Perhaps they had never seen someone so pretentious, simply explaining with a term that sounds grand.
That being said, it wasn’t just the interviewer who was stunned; I was actually confused too. As for what SPI extension really is and how it works, I didn’t understand at all back then.
However, today’s interviews are like this; if you want to impress the interviewer, you have to first impress yourself.
So today, let’s not talk about Spring’s SPI extension; let’s first look at what the Java SPI extension mechanism is all about.
1. Introduction
SPI stands for Service Provider Interface
, which translates to Service Provider Interface, and it actually implements a service discovery mechanism.
This might still be a bit hard to understand, so let me give you an analogy.
In a Spring project, before writing service layer code, it’s customary to add an interface layer. Then, through dependency injection in Spring, we can use @Autowired
and other methods to inject instances of the implementing classes of this interface, and subsequent service calls are generally based on interface operations.
In simple terms, it looks like this:

As shown in the figure, both the interface and the implementation class are provided by the service provider. We can consider the controller as the service caller, and the caller only needs to call the interface.
Although there are voices that suggest that in most cases, the service only has one implementation class, making the interface layer seem a bit redundant. However, in the book Head First Design Patterns, the authors still recommend:
Program to an interface, not an implementation.
That’s right, itās commonly said that you should program to interfaces. The benefits include reducing coupling, facilitating future extensions, and improving the flexibility and maintainability of the code.
In the example above, we can refer to this interface layer and its methods as API, while what we are discussing, SPI, has similarities as well as differences when compared to it. Let’s look at the diagram:

In simple terms, the service caller defines an interface specification that can be implemented by different service providers. Moreover, the caller can discover the service provider through a certain mechanism and invoke its capabilities via the interface.
By comparing, we can see that while they both have the concept of interface, there are still significant differences:
The interface in API is a functional list provided by the service provider to the service caller, while SPI emphasizes the constraints imposed by the service caller on the service implementation, which the service provider implements and can be discovered by the service caller.
In other words, what Java’s SPI implements is that you implement the service according to my interface specification, and I can find this service for this interface through a certain mechanism.
This might still sound abstract, so letās give an example to describe this process concretely.
A backend management system + user mini program implemented based on Spring Boot + MyBatis Plus + Vue & Element, supporting RBAC dynamic permissions, multi-tenancy, data permissions, workflows, third-party logins, payments, SMS, e-commerce, etc.
Project Address: https://github.com/YunaiV/ruoyi-vue-pro Video Tutorial: https://doc.iocoder.cn/video/
2. Define Interface
Speaking of smart home systems, everyone is quite familiar with them now; as long as the products are of the same brand, they can be controlled through a mobile app once connected to Wi-Fi, which is very convenient.
Even though products are constantly being updated and model numbers are emerging one after another, the functionality of the same type of home appliances operated through the app is generally the same. Take air conditioning as an example; we usually have just three main functions in the app: On/Off, Select Mode, and Adjust Temperature.
Suppose I have installed three different models of air conditioners in the living room, bedroom, and study, and connected them all to my app. Then, the subsequent operations are just the same few buttons, simple and straightforward.

Think about it; whether itās turning on/off or adjusting the temperature, itās just calling the deviceās interface through the app. If different models of air conditioners each have their own interfaces, the backend app would be a hassle to integrate with them.
The solution is simple; I first define a set of interface specifications. No matter what model of air conditioner you have in the future, you must implement the interface according to my specifications. As long as I can discover your device, I can call the interface in the same way.
Now let’s define such a set of interface specifications. If you want to connect to the smart home system in the future, you must follow this specification to develop the interface.
Create a new project as a standard, let’s call it aircondition-standard
, and then create an interface. Besides the three operations, we will also add a method to get the air conditioner model.
public interface IAircondition {
// Get Model
String getType();
// On/Off
void turnOnOff();
// Adjust Temperature
void adjustTemperature(int temperature);
// Change Mode
void changeModel(int modelId);
}
This interface will be used by the service providers, and we will package it into a jar with Maven:
mvn clean install
Then, the service provider can import this jar package into their project. With this specification, it ensures that no matter how the products are updated in the future, they can connect to the system.
A backend management system + user mini program implemented based on Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element, supporting RBAC dynamic permissions, multi-tenancy, data permissions, workflows, third-party logins, payments, SMS, e-commerce, etc.
Project Address: https://github.com/YunaiV/yudao-cloud Video Tutorial: https://doc.iocoder.cn/video/
3. Service Implementation
After formulating and publishing the rules, hanging air conditioners come as the first service provider. Create a new project aircondition-hanging-type
and import the jar package we just created:
<dependency>
<groupId>com.cn.hydra</groupId>
<artifactId>aircondition-standard</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
Create a service class and implement the previously defined interface:
public class HangingTypeAircondition implements IAircondition {
public String getType() {
return "HangingType";
}
public void turnOnOff() {
System.out.println("Hanging air conditioner on/off");
}
public void adjustTemperature(int i) {
System.out.println("Hanging air conditioner adjusting temperature");
}
public void changeModel(int i) {
System.out.println("Hanging air conditioner changing mode");
}
}
In the resources
directory of the project, create a META-INF/services
directory, then create a file named after the defined interface com.cn.hydra.IAircondition
, and write the full class name of the implementation class in the file.
com.cn.hydra.HangingTypeAircondition
The entire project structure is quite simple:

Thus, a simple implementation by a service provider is complete. Package it into a jar with Maven, and then it can be provided for the caller to use.
Similarly, we can create a project for vertical air conditioners aircondition-vertical-type
, and only create a service class:
public class VerticalTypeAircondition implements IAircondition {
public String getType() {
return "VerticalType";
}
public void turnOnOff() {
System.out.println("Vertical air conditioner on/off");
}
public void adjustTemperature(int i) {
System.out.println("Vertical air conditioner adjusting temperature");
}
public void changeModel(int i) {
System.out.println("Vertical air conditioner changing mode");
}
}
Still following the naming rules, create a configuration file:
com.cn.hydra.VerticalTypeAircondition
Again, package it into a jar, and thatās it. As for how the service caller discovers and invokes these two services, we will discuss it in detail below.
4. Service Discovery
Now that both service providers have implemented the interface, the next crucial step is service discovery, which is already accomplished by Java’s SPI discovery mechanism.
Create a new project aircondition-app
and import the two jar packages we created earlier.
<dependencies>
<dependency>
<groupId>com.cn.hydra</groupId>
<artifactId>aircondition-hanging-type</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.cn.hydra</groupId>
<artifactId>aircondition-vertical-type</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
According to what weāve discussed, although each service provider has different implementations for the interface, as a caller, you donāt need to care about the specific implementation classes. What we need to do is call the methods of the service provider through the interface.
Now comes the crucial service discovery step. We will write a method to call the corresponding air conditioner’s on/off method based on the model.
public class AirconditionApp {
public static void main(String[] args) {
new AirconditionApp().turnOn("VerticalType");
}
public void turnOn(String type) {
ServiceLoader<IAircondition> load = ServiceLoader.load(IAircondition.class);
for (IAircondition iAircondition : load) {
System.out.println("Detected:" + iAircondition.getClass().getSimpleName());
if (type.equals(iAircondition.getType())) {
iAircondition.turnOnOff();
}
}
}
}
Test results:

As you can see, during the test process, the defined interface IAircondition
discovered two implementation classes, and through parameters, called a specific implementation class’s method. Throughout the code, there were no concrete service implementation classes; all operations were done through the interface.
5. Principle
Having understood the workflow of SPI, letās take a look at its implementation. The key element is the ServiceLoader
class that appeared in the code above.
In the example code above, we used a for
loop to iterate over the result of the load()
method of the ServiceLoader
. We can understand this by looking at the source code, as ServiceLoader
implements the Iterable
interface, and the core of the service discovery lies in its iterator()
method.

Note that there are two key components here; letās find where they are defined in the source code:

The comments are very clear; providers
is a cache that will first look for in the iterator. If it finds it there, it continues looking; if not, it uses the lazy-loaded lookupIterator
to search.
So, itās simple. Letās look at the LazyIterator
and see how its hasNext()
and next()
methods are implemented.

This acc
is a security manager that is assigned through System.getSecurityManager()
. Debugging shows that it is all null
, so we only need to look at the hasNextService()
and nextService()
methods.
In the hasNextService()
method, the class name of the interface implementation class is taken out and placed into nextName
:

Next, in the nextService()
method, it first loads the implementation class and then instantiates the object, ultimately placing it into the cache.

During the iteration of the iterator, all implementation classes will be instantiated. Ultimately, it is still based on Java reflection.
6. Application
As for the practical application of SPI, the most common one should be the logging framework slf4j
, which uses SPI to achieve a plug-in style integration of other specific logging frameworks.
In simple terms, slf4j
itself is a logging facade that does not provide a specific implementation and needs to bind to other concrete implementations to truly introduce logging functionality.
For example, we can use log4j2
as a specific binder; we just need to include slf4j-log4j12
in the pom to use specific functionalities.
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.3</version>
</dependency>
After introducing the project, letās take a look at the specific structure of its jar package:

Have you discovered an Easter egg? Letās first explain why the slf4j-log4j12
we introduced in the pom actually refers to slf4j-reload4j
? Flipping through the official documentation:

The gist is that in 2015 and 2022, log4j1.x
announced its end of life
termination, which is not hard to guess, probably due to frequent vulnerabilities. After that, slf4j-log4j
will automatically redirect to slf4j-reload4j
during the build phase, and the official strongly recommends using slf4j-reload4j
as a replacement.
Looking back at the jar package’s META-INF.services
, the Reload4jServiceProvider
implementation class is injected through SPI. It implements the SLF4JServiceProvider
interface, and in its initialization method initialize()
, it completes initialization and other tasks, allowing access to LoggerFactory
and Logger
and other specific logging objects.
7. Summary
Java’s SPI provides a relatively unique service discovery and invocation mechanism, flexibly separating service invocation from service providers through interfaces, which is very convenient for providing extensions for third-party implementations. However, there are also drawbacks, such as once an interface is loaded, all implementation classes will be loaded, which may lead to loading unnecessary redundant services. Nevertheless, from an overall perspective, it still provides us with a very good idea for framework extension and integration.
Welcome to join my knowledge planet to comprehensively enhance your technical abilities.
š Joining method,“Long press” or “Scan” the QR code below:
The content of the planet includes: project practice, interview recruitment, source code analysis, learning routes.





If you find this article helpful, please share and support it.
Thank you for your support (*^__^*)