What Is the Principle of SPI in Meituan?

šŸ‘‰ This might be useful for you community

🐱 One-on-one communication/interview booklet/resume optimization/job hunting advice, welcome to joinYudao 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

What Is the Principle of SPI in Meituan?

šŸ‘‰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
Source: Programmer’s Arrival
  • 1. Introduction
  • 2. Define Interface
  • 3. Service Implementation
  • 4. Service Discovery
  • 5. Principle
  • 6. Application
  • 7. Summary
What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

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.

What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

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.

What Is the Principle of SPI in Meituan?

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

What Is the Principle of SPI in Meituan?

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.

What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

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

What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

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:

What Is the Principle of SPI in Meituan?

The content of the planet includes: project practice, interview recruitment, source code analysis, learning routes.

What Is the Principle of SPI in Meituan?
What Is the Principle of SPI in Meituan?What Is the Principle of SPI in Meituan?What Is the Principle of SPI in Meituan?What Is the Principle of SPI in Meituan?
If you find this article helpful, please share and support it.
Thank you for your support (*^__^*)

Leave a Comment